#include "level.h" #include "game_world.h" #include "physics_entity.h" #include "physics_world.h" #include "strutil.h" #include "variant.h" #include "ctype.h" static Dictionary internal_deserializers = { .list = { .data = NULL, .len = 0, .cap = 0, .element_size = 0 }, .element_size = 0 }; static int is_open_bracket(int c) { return c == '{'; } static int is_close_bracket(int c) { return c == '}'; } static int is_colon(int c) { return c == ':'; } static int ends_parameter(int c) { return c == '}' || c == ','; } int level_init() { internal_deserializers = dictionary_from_type(DeserializeFn); return 0; } int level_clean() { dictionary_empty(&internal_deserializers); return 0; } int level_register_spawner(const char* object_tag, DeserializeFn spawn_function) { dictionary_set_value(DeserializeFn, &internal_deserializers, object_tag, spawn_function); return 0; } static inline void spawn_object(DeserializeFn spawner, Dictionary* args) { BehaviourEntity entity = spawner(args); game_world_add_entity(entity); if(TC_MIRRORS(entity, PhysicsEntity)) physics_world_add_entity(TC_CAST(entity, PhysicsEntity)); } static inline long get_until(FILE* fp, char* out_buf, size_t buf_len, CharPredFn predfn) { if(feof(fp)) return 0; int c; char* write = out_buf; long count = 0; while(c != EOF) { // don't overflow if(out_buf != NULL && count + 1 >= buf_len) return count; // fetch next character c = fgetc(fp); // check if this is the desired character if(predfn(c)) { if(write != NULL) { *write = c; if(count + 2 < buf_len) *(write + 1) = '\0'; } return count + 1; } // skip space characters (unless search is a space character, which is handled above) if(isspace(c)) continue; // write and increment if(write != NULL) { *write = c; ++write; } ++count; } // EOF reached // write null terminator in place of EOF if possible if(write != NULL) *(write-1) = '\0'; return count + 1; } static inline long get_key(FILE* fp, char* out, size_t out_size) { long length = get_until(fp, out, out_size, is_colon); if(strfirst(out, out + length, '}') == 0) return 0; if(feof(fp)) return -1; length--; out[length] = '\0'; return length; } static inline Variant get_value(FILE* fp, char* buffer, size_t buffer_size, int* out_end_of_object) { long length = 0; *out_end_of_object = 0; do { length += get_until(fp, buffer+length, buffer_size-length, ends_parameter); if (length <= 0) return UndefinedVariant(); *out_end_of_object = strfirst(buffer, buffer + length, '}') != -1; } while(strcount(buffer, buffer+length, '(') != strcount(buffer, buffer+length, ')') && !(*out_end_of_object)); length--; buffer[length] = '\0'; return variant_from_str(buffer); } static inline int peek_next_non_blank(FILE* fp) { long position = ftell(fp); int c; do { c = fgetc(fp); } while(isspace(c)); fseek(fp, position, SEEK_SET); return c; } static inline int load_args(FILE* fp, Dictionary* out, char* buffer, size_t buffer_size) { char* key; long length; int is_end; do { length = get_key(fp, buffer, buffer_size); if(length == -1) // detect EOF before end of key return -1; key = malloc(length+1); strncpy(key, buffer, length); Variant var = get_value(fp, buffer, buffer_size, &is_end); dictionary_set_value(Variant, out, key, var); free(key); if(peek_next_non_blank(fp) == '}') { get_until(fp, NULL, 0, ends_parameter); break; } } while(!is_end); return 0; } int level_parse_file(FILE* fp) { // initialize a buffer string size_t length = 81; char buffer[length]; char* obj_tag = NULL; Dictionary args = dictionary_new(sizeof(Variant)); DeserializeFn spawner = NULL; do { // read the next line of the level file length = get_until(fp, buffer, sizeof(buffer)-1, is_open_bracket); --length; buffer[length] = '\0'; long start = strfirst(buffer, buffer+length, '}') + 1; // initialize object tag buffer obj_tag = malloc((length-start)+1); obj_tag[length - start] = '\0'; strncpy(obj_tag, buffer + start, length - start); // find the spawn function if(dictionary_try_get(&internal_deserializers, obj_tag, &spawner)) { // load arguments from file and spawn object load_args(fp, &args, buffer, sizeof(buffer)); spawn_object(spawner, &args); // clean up for next loop free(obj_tag); dictionary_empty(&args); } else { // clean up for next loop free(obj_tag); dictionary_empty(&args); obj_tag = NULL; // return -1; } } while(!feof(fp)); return 0; } int level_load_file(const char* path) { FILE* fp = fopen("assets/test.sc", "r"); level_parse_file(fp); return 0; }