diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-14 12:16:31 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-14 12:16:31 +0800 |
| commit | ee1aa5715693f7a8bd4886641dea0f6900a30eaa (patch) | |
| tree | ac29a783fafa5a469f438d0fb1b51d01667e2bb3 | |
| parent | 68565896993ac42769b02eb809c883a7a5726e00 (diff) | |
add mzscript and kons2c, converters write C directly through konscriptc
| -rw-r--r-- | tools/kons2c.c | 216 | ||||
| -rw-r--r-- | tools/konscriptc.h | 284 | ||||
| -rw-r--r-- | tools/mzscript.c | 129 |
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++) { |
