You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
433 lines
15 KiB
C
433 lines
15 KiB
C
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
|
|
#define assert_cond(cond, explanation) { if(!cond) { printf("Codegen assert_condion line %d %s failed: %.*s\n", __LINE__, #cond, MD_S8VArg(explanation)); __debugbreak(); exit(1); } }
|
|
|
|
#include "buff.h"
|
|
|
|
#pragma warning(disable : 4996) // nonsense about fopen being insecure
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4244) // loss of data warning
|
|
#pragma warning(disable : 4101) // unreferenced local variable
|
|
#include "md.h"
|
|
#include "md.c"
|
|
#pragma warning(pop)
|
|
|
|
|
|
MD_String8 OUTPUT_FOLDER = MD_S8LitComp("gen"); // no trailing slash
|
|
MD_String8 ASSETS_FOLDER = MD_S8LitComp("assets");
|
|
|
|
#define log(...) { printf("Codegen: "); printf(__VA_ARGS__); }
|
|
|
|
void dump(MD_Node* from) {
|
|
printf("/ %.*s\n", MD_S8VArg(from->string));
|
|
int d = 0;
|
|
for(MD_EachNode(child, from->first_child))
|
|
{
|
|
printf("|-- Child %d Tag [%.*s] string[%.*s] first child string[%.*s]\n", d, MD_S8VArg(child->first_tag->string), MD_S8VArg(child->string), MD_S8VArg(child->first_child->string));
|
|
d += 1;
|
|
}
|
|
}
|
|
|
|
void dump_root(MD_Node* from) {
|
|
// Iterate through each top-level node
|
|
for(MD_EachNode(node, from->first_child))
|
|
{
|
|
printf("/ %.*s\n", MD_S8VArg(node->string));
|
|
|
|
// Print the name of each of the node's tags
|
|
for(MD_EachNode(tag, node->first_tag))
|
|
{
|
|
printf("|-- Tag %.*s\n", MD_S8VArg(tag->string));
|
|
}
|
|
|
|
// Print the name of each of the node's children
|
|
for(MD_EachNode(child, node->first_child))
|
|
{
|
|
printf("|-- Child %.*s\n", MD_S8VArg(child->string));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool has_decimal(MD_String8 s)
|
|
{
|
|
for(int i = 0; i < s.size; i++)
|
|
{
|
|
if(s.str[i] == '.') return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
MD_Arena *cg_arena = NULL;
|
|
|
|
MD_String8 ChildValue(MD_Node *n, MD_String8 name) {
|
|
MD_Node *child_with_value = MD_ChildFromString(n, name, 0);
|
|
assert_cond(child_with_value, MD_S8Fmt(cg_arena, "Could not find child named '%.*s' of node '%.*s'", MD_S8VArg(name), MD_S8VArg(n->string)));
|
|
assert_cond(child_with_value->first_child, MD_S8Lit("Must have child"));
|
|
return child_with_value->first_child->string;
|
|
}
|
|
|
|
MD_String8 asset_file_path(MD_String8 filename) {
|
|
return MD_S8Fmt(cg_arena, "%.*s/%.*s", MD_S8VArg(ASSETS_FOLDER), MD_S8VArg(filename));
|
|
}
|
|
|
|
char *nullterm(MD_String8 s) {
|
|
char *to_return = malloc(s.size + 1);
|
|
memcpy(to_return, s.str, s.size);
|
|
to_return[s.size] = '\0';
|
|
return to_return;
|
|
}
|
|
|
|
char* fillnull(char *s, char c) {
|
|
while(*s != '\0') {
|
|
if(*s == c) {
|
|
*s = '\0';
|
|
return s + 1;
|
|
}
|
|
s++;
|
|
}
|
|
assert_cond(false, MD_S8Lit("Couldn't find char"));
|
|
return NULL;
|
|
}
|
|
|
|
#define StrSame(s1, s2) MD_S8Match((s1), (s2), 0)
|
|
|
|
typedef BUFF(MD_Node*, 256) Nodes;
|
|
MD_Node* find_by_name(Nodes *n, MD_String8 name)
|
|
{
|
|
MD_Node *node_with = 0;
|
|
BUFF_ITER(MD_Node *, n)
|
|
{
|
|
if(StrSame((*it)->string, name))
|
|
{
|
|
assert(node_with == 0);
|
|
node_with = (*it);
|
|
}
|
|
}
|
|
assert(node_with);
|
|
return node_with;
|
|
}
|
|
|
|
char* goto_end_of(char *tomove, size_t max_move, char *pattern) {
|
|
size_t pattern_len = strlen(pattern);
|
|
for(int i = 0; i < max_move; i++) {
|
|
if(strncmp(tomove, pattern, pattern_len) == 0) {
|
|
tomove += pattern_len;
|
|
return tomove;
|
|
}
|
|
tomove++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#define list_printf(list_ptr, ...) MD_S8ListPush(cg_arena, list_ptr, MD_S8Fmt(cg_arena, __VA_ARGS__))
|
|
#define S8(s) MD_S8Lit(s)
|
|
|
|
int main(int argc, char **argv) {
|
|
cg_arena = MD_ArenaAlloc();
|
|
assert_cond(cg_arena, MD_S8Lit("Memory"));
|
|
|
|
char *nulled = nullterm(MD_S8Lit("test"));
|
|
|
|
MD_ParseResult training_parse = MD_ParseWholeFile(cg_arena, MD_S8Lit("training.mdesk"));
|
|
MD_String8 global_prompt = {0};
|
|
for(MD_EachNode(node, training_parse.node->first_child))
|
|
{
|
|
if(MD_S8Match(node->first_tag->string, MD_S8Lit("global_prompt"), 0))
|
|
{
|
|
global_prompt = node->string;
|
|
}
|
|
}
|
|
MD_String8 training_writeto = MD_S8Fmt(cg_arena, "%.*s/training_data.jsonl\0", MD_S8VArg(OUTPUT_FOLDER));
|
|
FILE *train = fopen(training_writeto.str, "w");
|
|
assert(train);
|
|
|
|
Nodes characters = {0};
|
|
for(MD_EachNode(node, training_parse.node->first_child))
|
|
{
|
|
if(MD_S8Match(node->first_tag->string, MD_S8Lit("character"), 0))
|
|
{
|
|
BUFF_APPEND(&characters, node);
|
|
}
|
|
}
|
|
|
|
Nodes items = {0};
|
|
for(MD_EachNode(node, training_parse.node->first_child))
|
|
{
|
|
if(MD_S8Match(node->first_tag->string, MD_S8Lit("item"), 0))
|
|
{
|
|
BUFF_APPEND(&items, node);
|
|
}
|
|
}
|
|
|
|
for(MD_EachNode(node, training_parse.node->first_child))
|
|
{
|
|
if(MD_S8Match(node->first_tag->string, MD_S8Lit("training"), 0))
|
|
{
|
|
MD_Node *node_with = find_by_name(&characters, ChildValue(node, S8("with")));
|
|
|
|
MD_String8List item_prompts = {0};
|
|
MD_Node *items_node = MD_ChildFromString(node, S8("items"), 0);
|
|
if(!MD_NodeIsNil(items_node))
|
|
{
|
|
for(MD_EachNode(item_name_node, items_node->first_child))
|
|
{
|
|
MD_String8 item_prompt = ChildValue(find_by_name(&items, item_name_node->string), S8("prompt"));
|
|
MD_S8ListPush(cg_arena, &item_prompts, item_prompt);
|
|
}
|
|
}
|
|
MD_StringJoin join = (MD_StringJoin){ .mid = S8("\\n") };
|
|
MD_String8 items_prompt = S8("");
|
|
if(item_prompts.node_count > 0)
|
|
{
|
|
MD_S8ListPush(cg_arena, &item_prompts, S8(""));
|
|
items_prompt = MD_S8ListJoin(cg_arena, item_prompts, &join);
|
|
}
|
|
|
|
int num_sentences = 0;
|
|
for(MD_EachNode(s, MD_ChildFromString(node, S8("data"), 0)->first_child))
|
|
{
|
|
num_sentences += 1;
|
|
}
|
|
assert(num_sentences >= 2);
|
|
assert(num_sentences % 2 == 0);
|
|
|
|
for(int upto_length = 2; upto_length <= num_sentences; upto_length += 2)
|
|
{
|
|
fprintf(train, "{\"prompt\": \"");
|
|
fprintf(train, nullterm(global_prompt), MD_S8VArg(ChildValue(node_with, S8("actions_str"))));
|
|
fprintf(train, "\\n");
|
|
fprintf(train, "%.*s", MD_S8VArg(items_prompt));
|
|
fprintf(train, "%.*s\\n", MD_S8VArg(ChildValue(node_with, S8("prompt"))));
|
|
MD_Node *cur_sentence = MD_ChildFromString(node, S8("data"), 0)->first_child;
|
|
MD_String8 completion = {0};
|
|
for(int i = 0; i < upto_length; i += 1)
|
|
{
|
|
assert(!MD_NodeIsNil(cur_sentence));
|
|
if(i % 2 == 0)
|
|
{
|
|
fprintf(train, "Player: \\\"%.*s\\\"\\n", MD_S8VArg(cur_sentence->string));
|
|
}
|
|
else
|
|
{
|
|
fprintf(train, "%.*s: \\\"", MD_S8VArg(ChildValue(node_with, S8("name"))));
|
|
if(i == upto_length - 1)
|
|
{
|
|
completion = cur_sentence->string;
|
|
}
|
|
else
|
|
{
|
|
fprintf(train, "%.*s\\\"\\n", MD_S8VArg(cur_sentence->string));
|
|
}
|
|
|
|
}
|
|
cur_sentence = cur_sentence->next;
|
|
}
|
|
fprintf(train, "\", \"completion\": \"%.*s\\\"\"}\n", MD_S8VArg(completion));
|
|
}
|
|
}
|
|
}
|
|
|
|
BUFF_ITER(MD_Node*, &characters)
|
|
{
|
|
printf("Character %.*s\n", MD_S8VArg((*it)->string));
|
|
}
|
|
|
|
|
|
// I hope to God MD_String8's are null terminated...
|
|
MD_String8 writeto = MD_S8Fmt(cg_arena, "%.*s/assets.gen.c", MD_S8VArg(OUTPUT_FOLDER));
|
|
log("Writing to %.*s\n", MD_S8VArg(writeto));
|
|
FILE *output = fopen(writeto.str, "w");
|
|
|
|
MD_ParseResult parse = MD_ParseWholeFile(cg_arena, MD_S8Lit("assets.mdesk"));
|
|
|
|
//dump(parse.node);
|
|
|
|
MD_String8List declarations_list = {0};
|
|
MD_String8List load_list = {0};
|
|
MD_String8List level_decl_list = {0};
|
|
MD_String8List tileset_decls = {0};
|
|
for(MD_EachNode(node, parse.node->first_child)) {
|
|
if(MD_S8Match(node->first_tag->string, MD_S8Lit("image"), 0)) {
|
|
MD_String8 variable_name = MD_S8Fmt(cg_arena, "image_%.*s", MD_S8VArg(node->string));
|
|
log("New image variable %.*s\n", MD_S8VArg(variable_name));
|
|
MD_String8 filepath = ChildValue(node, MD_S8Lit("filepath"));
|
|
filepath = asset_file_path(filepath);
|
|
assert_cond(filepath.str != 0, MD_S8Fmt(cg_arena, "No filepath specified for image '%.*s'", MD_S8VArg(node->string)));
|
|
FILE *asset_file = fopen(filepath.str, "r");
|
|
assert_cond(asset_file, MD_S8Fmt(cg_arena, "Could not open filepath %.*s for asset '%.*s'", MD_S8VArg(filepath), MD_S8VArg(node->string)));
|
|
fclose(asset_file);
|
|
|
|
MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name)));
|
|
MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(\"%.*s\");\n", MD_S8VArg(variable_name), MD_S8VArg(filepath)));
|
|
}
|
|
if(MD_S8Match(node->first_tag->string, MD_S8Lit("tileset"), 0)) {
|
|
// not a variable anymore
|
|
MD_String8 variable_name = MD_S8Fmt(cg_arena, "tileset_%.*s", MD_S8VArg(node->string));
|
|
log("New tileset %.*s\n", MD_S8VArg(variable_name));
|
|
MD_String8 filepath = asset_file_path(ChildValue(node, MD_S8Lit("filepath")));
|
|
|
|
MD_String8 tileset_file_contents = MD_LoadEntireFile(cg_arena, filepath);
|
|
list_printf(&tileset_decls, "{\n", MD_S8VArg(variable_name));
|
|
list_printf(&tileset_decls, ".first_gid = %.*s,\n", MD_S8VArg(ChildValue(node, MD_S8Lit("firstgid"))));
|
|
list_printf(&tileset_decls, ".img = &%.*s,\n", MD_S8VArg(ChildValue(node, MD_S8Lit("image"))));
|
|
|
|
list_printf(&tileset_decls, ".animated = {\n");
|
|
char *end = tileset_file_contents.str + tileset_file_contents.size;
|
|
char *cur = tileset_file_contents.str;
|
|
int num_animated_tiles = 0;
|
|
while(cur < end) {
|
|
cur = goto_end_of(cur, end - cur, "<tile id=\"");
|
|
if(cur == NULL) break;
|
|
char *end_of_anim = goto_end_of(cur, end - cur, "</animation>");
|
|
if(end_of_anim == NULL) break;
|
|
char *new_cur = fillnull(cur, '"');
|
|
int frame_from = atoi(cur);
|
|
cur = new_cur;
|
|
list_printf(&tileset_decls, "{ .id_from = %d, .frames = { ", frame_from);
|
|
|
|
int num_frames = 0;
|
|
while(true) {
|
|
char *next_frame = goto_end_of(cur, end - cur, "<frame tileid=\"");
|
|
if(end_of_anim == NULL || next_frame == NULL || next_frame > end_of_anim) break;
|
|
char *new_cur = fillnull(next_frame, '"');
|
|
int frame = atoi(next_frame);
|
|
|
|
list_printf(&tileset_decls, "%d, ", frame);
|
|
num_frames++;
|
|
|
|
cur = new_cur;
|
|
}
|
|
list_printf(&tileset_decls, "}, .num_frames = %d },\n", num_frames);
|
|
num_animated_tiles++;
|
|
}
|
|
if(num_animated_tiles == 0) list_printf(&tileset_decls, "0");
|
|
list_printf(&tileset_decls, "}},\n");
|
|
}
|
|
if(MD_S8Match(node->first_tag->string, MD_S8Lit("level"), 0)) {
|
|
MD_String8 variable_name = MD_S8Fmt(cg_arena, "level_%.*s", MD_S8VArg(node->string));
|
|
log("New level variable %.*s\n", MD_S8VArg(variable_name));
|
|
MD_String8 filepath = asset_file_path(ChildValue(node, MD_S8Lit("filepath")));
|
|
MD_ParseResult level_parse = MD_ParseWholeFile(cg_arena, filepath);
|
|
assert_cond(!MD_NodeIsNil(level_parse.node->first_child), MD_S8Lit("Failed to load level file"));
|
|
|
|
MD_Node *layers = MD_ChildFromString(level_parse.node->first_child, MD_S8Lit("layers"), 0);
|
|
fprintf(output, "Level %.*s = {\n", MD_S8VArg(variable_name));
|
|
MD_String8List tile_layer_decls = {0};
|
|
for(MD_EachNode(lay, layers->first_child)) {
|
|
MD_String8 type = MD_ChildFromString(lay, MD_S8Lit("type"), 0)->first_child->string;
|
|
if(MD_S8Match(type, MD_S8Lit("objectgroup"), 0)) {
|
|
fprintf(output, ".initial_entities = {\n");
|
|
for(MD_EachNode(object, MD_ChildFromString(lay, MD_S8Lit("objects"), 0)->first_child)) {
|
|
//dump(object);
|
|
// negative numbers for object position aren't supported here
|
|
MD_String8 name = MD_ChildFromString(object, MD_S8Lit("name"), 0)->first_child->string;
|
|
MD_String8 x_string = MD_ChildFromString(object, MD_S8Lit("x"), 0)->first_child->string;
|
|
MD_String8 y_string = MD_ChildFromString(object, MD_S8Lit("y"), 0)->first_child->string;
|
|
y_string = MD_S8Fmt(cg_arena, "-%.*s", MD_S8VArg(y_string));
|
|
|
|
if(has_decimal(x_string)) x_string = MD_S8Fmt(cg_arena, "%.*sf", MD_S8VArg(x_string));
|
|
if(has_decimal(y_string)) y_string = MD_S8Fmt(cg_arena, "%.*sf", MD_S8VArg(y_string));
|
|
|
|
MD_String8 class = MD_ChildFromString(object, MD_S8Lit("class"), 0)->first_child->string;
|
|
if(MD_S8Match(class, MD_S8Lit("PROP"), 0))
|
|
{
|
|
fprintf(output, "{ .exists = true, .is_prop = true, .prop_kind = %.*s, .pos = { .X=%.*s, .Y=%.*s }, }, ", MD_S8VArg(name), MD_S8VArg(x_string), MD_S8VArg(y_string));
|
|
}
|
|
else if(MD_S8Match(name, MD_S8Lit("PLAYER"), 0))
|
|
{
|
|
fprintf(output, "{ .exists = true, .is_character = true, .pos = { .X=%.*s, .Y=%.*s }, }, ", MD_S8VArg(x_string), MD_S8VArg(y_string));
|
|
}
|
|
else
|
|
{
|
|
fprintf(output, "{ .exists = true, .is_npc = true, .npc_kind = NPC_%.*s, .pos = { .X=%.*s, .Y=%.*s }, }, ", MD_S8VArg(name), MD_S8VArg(x_string), MD_S8VArg(y_string));
|
|
}
|
|
}
|
|
fprintf(output, "\n}, // entities\n");
|
|
}
|
|
if(MD_S8Match(type, MD_S8Lit("tilelayer"), 0)) {
|
|
int width = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("width"), 0)->first_child->string));
|
|
int height = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("height"), 0)->first_child->string));
|
|
MD_Node *data = MD_ChildFromString(lay, MD_S8Lit("data"), 0);
|
|
|
|
int num_index = 0;
|
|
MD_String8List cur_layer_decl = {0};
|
|
list_printf(&cur_layer_decl, "{ \n");
|
|
list_printf(&cur_layer_decl, "{ ");
|
|
for(MD_EachNode(tile_id_node, data->first_child)) {
|
|
list_printf(&cur_layer_decl, "%.*s, ", MD_S8VArg(tile_id_node->string));
|
|
|
|
if(num_index % width == width - 1) {
|
|
if(MD_NodeIsNil(tile_id_node->next)) {
|
|
list_printf(&cur_layer_decl, "},\n}, // tiles for this layer\n");
|
|
} else {
|
|
list_printf(&cur_layer_decl, "},\n{ ");
|
|
}
|
|
}
|
|
num_index += 1;
|
|
}
|
|
|
|
MD_StringJoin join = MD_ZERO_STRUCT;
|
|
MD_String8 layer_decl_string = MD_S8ListJoin(cg_arena, cur_layer_decl, &join);
|
|
MD_S8ListPush(cg_arena, &tile_layer_decls, layer_decl_string);
|
|
}
|
|
}
|
|
|
|
fprintf(output, ".tiles = {\n");
|
|
// layer decls
|
|
{
|
|
|
|
MD_StringJoin join = MD_ZERO_STRUCT;
|
|
MD_String8 layers_string = MD_S8ListJoin(cg_arena, tile_layer_decls, &join);
|
|
fprintf(output, "%.*s\n", MD_S8VArg(layers_string));
|
|
//MD_S8ListPush(cg_arena, &tile_layer_delcs, layer_decl_string);
|
|
|
|
}
|
|
fprintf(output, "} // tiles\n");
|
|
|
|
fprintf(output, "\n}; // %.*s\n", MD_S8VArg(variable_name));
|
|
}
|
|
}
|
|
|
|
MD_StringJoin join = MD_ZERO_STRUCT;
|
|
MD_String8 declarations = MD_S8ListJoin(cg_arena, declarations_list, &join);
|
|
MD_String8 loads = MD_S8ListJoin(cg_arena, load_list, &join);
|
|
fprintf(output, "%.*s\nvoid load_assets() {\n%.*s\n}\n", MD_S8VArg(declarations), MD_S8VArg(loads));
|
|
|
|
fprintf(output, "TileSet tilesets[] = { %.*s\n };\n", MD_S8VArg(MD_S8ListJoin(cg_arena, tileset_decls, &join)));
|
|
|
|
|
|
fclose(output);
|
|
|
|
output = fopen(MD_S8Fmt(cg_arena, "%.*s/characters.gen.h\0", MD_S8VArg(OUTPUT_FOLDER)).str, "w");
|
|
//fprintf(output, "char *global_prompt = \"%.*s\";\n", MD_S8VArg(global_prompt));
|
|
fprintf(output, "char *prompt_table[] = {\n");
|
|
BUFF_ITER(MD_Node*, &characters)
|
|
{
|
|
MD_String8 personalized_global_prompt = MD_S8Fmt(cg_arena, nullterm(global_prompt), MD_S8VArg(ChildValue(*it, S8("actions_str"))));
|
|
fprintf(output, "\"%.*s\\n%.*s\", // %.*s\n", MD_S8VArg(personalized_global_prompt), MD_S8VArg(ChildValue(*it, S8("prompt"))),MD_S8VArg((*it)->string));
|
|
}
|
|
fprintf(output, "}; // prompt table\n");
|
|
|
|
fprintf(output, "char *name_table[] = {\n");
|
|
BUFF_ITER(MD_Node*, &characters)
|
|
{
|
|
fprintf(output, "\"%.*s\", // %.*s\n", MD_S8VArg(ChildValue(*it, S8("name"))),MD_S8VArg((*it)->string));
|
|
}
|
|
fprintf(output, "}; // name table\n");
|
|
|
|
fprintf(output, "typedef enum\n{ // character enums, not completed here so you can add more in the include\n");
|
|
BUFF_ITER(MD_Node*, &characters)
|
|
{
|
|
fprintf(output, "NPC_%.*s,\n", MD_S8VArg((*it)->string));
|
|
}
|
|
//fprintf(output, "NPC_LAST_CHARACTER,\n};\n");
|
|
|
|
|
|
fclose(output);
|
|
|
|
return 0;
|
|
}
|