From 908181fac16455bdfefc7186156ac136616c9590 Mon Sep 17 00:00:00 2001 From: shariq Date: Sun, 18 Jun 2017 17:52:36 +0500 Subject: [PATCH] Implemented initial version of loading entities from file --- src/entity.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++-- src/entity.h | 2 + src/game.c | 39 +++---- src/light.c | 6 ++ src/light.h | 1 + src/material.c | 21 ++-- src/material.h | 6 +- src/model.c | 4 +- src/variant.c | 27 ++++- src/variant.h | 9 +- 10 files changed, 342 insertions(+), 51 deletions(-) diff --git a/src/entity.c b/src/entity.c index 04d5f95..30e55d4 100644 --- a/src/entity.c +++ b/src/entity.c @@ -9,6 +9,7 @@ #include "sound.h" #include "material.h" #include "geometry.h" +#include "variant.h" #include "file_io.h" #include @@ -16,6 +17,10 @@ #include #include +#define MAX_ENTITY_PROP_NAME_LEN 128 +#define MAX_ENTITY_PROP_LEN 256 +#define MAX_LINE_LEN 512 + static struct Entity* entity_list; static int* empty_indices; @@ -172,8 +177,8 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type /* First write all properties common to all entity types */ fprintf(entity_file, "name: %s\n", entity->name); fprintf(entity_file, "type: %d\n", entity->type); - fprintf(entity_file, "is_listener: %d\n", entity->is_listener); - fprintf(entity_file, "renderable: %d\n", entity->renderable); + fprintf(entity_file, "is_listener: %s\n", entity->is_listener ? "true" : "false"); + fprintf(entity_file, "renderable: %s\n", entity->renderable ? "true" : "false"); struct Entity* parent = entity_get_parent(entity->id); fprintf(entity_file, "parent: %s\n", parent->name); @@ -197,12 +202,12 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type { case ET_CAMERA: { - fprintf(entity_file, "ortho: %d\n", entity->camera.ortho); - fprintf(entity_file, "resizeable: %d\n", entity->camera.resizeable); + fprintf(entity_file, "ortho: %s\n", entity->camera.ortho ? "true" : "false"); + fprintf(entity_file, "resizeable: %s\n", entity->camera.resizeable ? "true" : "false"); fprintf(entity_file, "fov: %.5f\n", entity->camera.fov); fprintf(entity_file, "nearz: %.5f\n", entity->camera.nearz); fprintf(entity_file, "farz: %.5f\n", entity->camera.farz); - fprintf(entity_file, "render_texture: %d\n", entity->camera.render_tex == -1 ? 0 : 1); + fprintf(entity_file, "render_texture: %s\n", entity->camera.render_tex == -1 ? "false" : "true"); break; } case ET_STATIC_MESH: @@ -216,16 +221,16 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type } case ET_LIGHT: { - fprintf(entity_file, "type: %df\n", entity->light.valid); + fprintf(entity_file, "light_type: %d\n", entity->light.valid); fprintf(entity_file, "outer_angle: %.5f\n", entity->light.outer_angle); fprintf(entity_file, "inner_angle: %.5f\n", entity->light.inner_angle); fprintf(entity_file, "falloff: %.5f\n", entity->light.falloff); fprintf(entity_file, "radius: %d\n", entity->light.radius); fprintf(entity_file, "intensity: %.5f\n", entity->light.intensity); fprintf(entity_file, "depth_bias: %.5f\n", entity->light.depth_bias); - fprintf(entity_file, "valid: %df\n", entity->light.valid); - fprintf(entity_file, "cast_shadow: %df\n", entity->light.cast_shadow); - fprintf(entity_file, "pcf_enabled: %df\n", entity->light.pcf_enabled); + fprintf(entity_file, "valid: %s\n", entity->light.valid ? "true" : "false"); + fprintf(entity_file, "cast_shadow: %s\n", entity->light.cast_shadow ? "true" : "false"); + fprintf(entity_file, "pcf_enabled: %s\n", entity->light.pcf_enabled ? "true" : "false"); fprintf(entity_file, "color: %.5f %.5f %.5f", entity->light.color.x, entity->light.color.y, @@ -234,8 +239,8 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type } case ET_SOUND_SOURCE: { - fprintf(entity_file, "active: %df\n", entity->sound_source.active); - fprintf(entity_file, "relative: %df\n", entity->sound_source.relative); + fprintf(entity_file, "active: %s\n", entity->sound_source.active ? "true" : "false"); + fprintf(entity_file, "relative: %s\n", entity->sound_source.relative ? "true" : "false"); break; } }; @@ -246,3 +251,254 @@ bool entity_save(struct Entity* entity, const char* filename, int directory_type fclose(entity_file); return success; } + +struct Entity* entity_load(const char* filename, int directory_type) +{ + FILE* entity_file = io_file_open(directory_type, filename, "r"); + if(!entity_file) + { + log_error("entity:load", "Failed to open entity file %s for writing"); + return NULL; + } + + struct Entity entity = + { + .id = -1, + .type = ET_NONE, + .is_listener = false, + .renderable = false, + .marked_for_deletion = false, + .name = NULL + }; + + char* material_name = NULL; + char* entity_name = NULL; + char* geometry_name = NULL; + char* parent_name = NULL; + static struct Variant var_value = { .type = VT_NONE}; + char prop_str[MAX_ENTITY_PROP_NAME_LEN]; + char line_buffer[MAX_LINE_LEN]; + memset(prop_str, '\0', MAX_ENTITY_PROP_NAME_LEN); + memset(line_buffer, '\0', MAX_LINE_LEN); + int current_line = 0; + variant_free(&var_value); + while(fgets(line_buffer, MAX_LINE_LEN -1, entity_file)) + { + current_line++; + memset(prop_str, '\0', MAX_ENTITY_PROP_NAME_LEN); + + if(line_buffer[0] == '#') continue; + if(strlen(line_buffer) == 0) break; + + char* value_str = strstr(line_buffer, ":"); + if(!value_str) + { + log_warning("Malformed value in entity file %s, line %d", filename, current_line); + continue; + } + + value_str++; /* Ignore the colon(:) and set the pointer after it */ + + if(sscanf(line_buffer, " %1024[^: ] : %*s", prop_str) != 1) + { + log_warning("Unable to read property name in entity file %s, line %d", filename, current_line); + continue; + } + + /* Common entity properties */ + if(strncmp("name", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_STR); + entity_name = str_new(var_value.val_str); + //variant_copy_out(&entity.name, &var_value); + } + if(strncmp("parent", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_STR); + parent_name = str_new(var_value.val_str); + //variant_copy_out(&entity.name, &var_value); + } + else if(strncmp("type", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_INT); + variant_copy_out(&entity.type, &var_value); + } + else if(strncmp("is_listener", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.is_listener, &var_value); + } + else if(strncmp("renderable", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.renderable, &var_value); + } + + /* Transform */ + else if(strncmp("position", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_VEC3); + variant_copy_out(&entity.transform.position, &var_value); + } + else if(strncmp("scale", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_VEC3); + variant_copy_out(&entity.transform.scale, &var_value); + } + else if(strncmp("rotation", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_QUAT); + variant_copy_out(&entity.transform.rotation, &var_value); + } + + /* Camera */ + else if(strncmp("ortho", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.camera.ortho, &var_value); + } + else if(strncmp("resizeable", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.camera.resizeable, &var_value); + } + else if(strncmp("fov", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_FLOAT); + variant_copy_out(&entity.camera.fov, &var_value); + } + else if(strncmp("nearz", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_FLOAT); + variant_copy_out(&entity.camera.nearz, &var_value); + } + else if(strncmp("farz", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_FLOAT); + variant_copy_out(&entity.camera.farz, &var_value); + } + else if(strncmp("render_texture", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.camera.fbo, &var_value); + } + + /* Light */ + else if(strncmp("light_type", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_INT); + variant_copy_out(&entity.light.type, &var_value); + } + else if(strncmp("outer_angle", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_FLOAT); + variant_copy_out(&entity.light.outer_angle, &var_value); + } + else if(strncmp("inner_angle", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_FLOAT); + variant_copy_out(&entity.light.inner_angle, &var_value); + } + else if(strncmp("falloff", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_FLOAT); + variant_copy_out(&entity.light.falloff, &var_value); + } + else if(strncmp("radius", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_INT); + variant_copy_out(&entity.light.radius, &var_value); + } + else if(strncmp("intensity", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_FLOAT); + variant_copy_out(&entity.light.intensity, &var_value); + } + else if(strncmp("depth_bias", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_FLOAT); + variant_copy_out(&entity.light.depth_bias, &var_value); + } + else if(strncmp("valid", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.light.valid, &var_value); + } + else if(strncmp("cast_shadow", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.light.cast_shadow, &var_value); + } + else if(strncmp("pcf_enabled", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.light.pcf_enabled, &var_value); + } + else if(strncmp("color", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_VEC3); + variant_copy_out(&entity.light.color, &var_value); + } + + /* Model */ + else if(strncmp("material", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_STR); + material_name = str_new(var_value.val_str); + } + else if(strncmp("geometry", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_STR); + geometry_name = str_new(var_value.val_str); + } + + /* Sound Source */ + else if(strncmp("active", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.sound_source.active, &var_value); + } + else if(strncmp("relative", prop_str, MAX_ENTITY_PROP_NAME_LEN) == 0) + { + variant_from_str(&var_value, value_str, VT_BOOL); + variant_copy_out(&entity.sound_source.relative, &var_value); + } + + variant_free(&var_value); + } + + /* Do the things after assignment */ + struct Entity* parent_entity = entity_find(parent_name); + struct Entity* new_entity = entity_create(entity_name, entity.type, parent_entity ? parent_entity->id : -1); + free(entity_name); + transform_translate(new_entity, &entity.transform.position, TS_PARENT); + quat_assign(&new_entity->transform.rotation, &entity.transform.rotation); + transform_scale(new_entity, &entity.transform.scale); + + if(entity.is_listener) sound_listener_set(new_entity->id); + if(entity.renderable) new_entity->renderable = true; + + switch(new_entity->type) + { + case ET_CAMERA: + camera_update_view(new_entity); + camera_update_proj(new_entity); + break; + case ET_STATIC_MESH: + model_create(new_entity, geometry_name, material_name); + free(geometry_name); + free(material_name); + break; + case ET_LIGHT: + memcpy(&new_entity->light, &entity.light, sizeof(struct Light)); + light_add(new_entity); + break; + case ET_SOUND_SOURCE: + sound_source_create(new_entity, new_entity->sound_source.relative); + break; + }; + + log_message("Entity %s loaded from %s", new_entity->name, filename); + fclose(entity_file); + return new_entity; +} diff --git a/src/entity.h b/src/entity.h index aa2abd4..e9561d4 100644 --- a/src/entity.h +++ b/src/entity.h @@ -8,6 +8,7 @@ struct Material_Param; enum Entity_Type { + ET_NONE, ET_PLAYER, ET_ROOT, ET_CAMERA, @@ -121,5 +122,6 @@ struct Entity* entity_find(const char* name); struct Entity* entity_get_all(void); struct Entity* entity_get_parent(int node); bool entity_save(struct Entity* entity, const char* filename, int directory_type); +struct Entity* entity_load(const char* filename, int directory_type); #endif diff --git a/src/game.c b/src/game.c index 764c47b..c9adc35 100644 --- a/src/game.c +++ b/src/game.c @@ -166,24 +166,27 @@ void scene_setup(void) /* model_set_material_param(screen_model, "diffuse_color", &color); */ /* model_set_material_param(screen_model, "diffuse_texture", &cam->render_tex); */ - const int MAX_LIGHTS = 6; - for(int i = 0; i < MAX_LIGHTS; i++) - { - int x = rand() % MAX_LIGHTS; - int z = rand() % MAX_LIGHTS; - x++; z++; - struct Entity* light_ent = scene_add_new("Light_Ent", ET_LIGHT); - vec3 lt_pos = {x * 20, 0, z * 20}; - transform_set_position(light_ent, <_pos); - light_create(light_ent, LT_POINT); - vec3_fill(&light_ent->light.color, 1.f / (float)x, 1.f / ((rand() % 10) + 1.f), 1.f / (float)z); - light_ent->light.intensity = 1.f; - } - - log_message("Sizeof Entity : %d", sizeof(struct Entity)); - - struct Entity* light_ent = entity_find("Light_Ent"); - entity_save(light_ent, "light.ent", DT_INSTALL); + /* const int MAX_LIGHTS = 6; */ + /* for(int i = 0; i < MAX_LIGHTS; i++) */ + /* { */ + /* int x = rand() % MAX_LIGHTS; */ + /* int z = rand() % MAX_LIGHTS; */ + /* x++; z++; */ + /* struct Entity* light_ent = scene_add_new("Light_Ent", ET_LIGHT); */ + /* vec3 lt_pos = {x * 20, 0, z * 20}; */ + /* transform_set_position(light_ent, <_pos); */ + /* light_create(light_ent, LT_POINT); */ + /* vec3_fill(&light_ent->light.color, 1.f / (float)x, 1.f / ((rand() % 10) + 1.f), 1.f / (float)z); */ + /* light_ent->light.intensity = 1.f; */ + /* } */ + + /* log_message("Sizeof Entity : %d", sizeof(struct Entity)); */ + + /* struct Entity* light_ent = entity_find("Ground"); */ + /* entity_save(light_ent, "ground.ent", DT_INSTALL); */ + + struct Entity* light = entity_load("light.ent", DT_INSTALL); + log_message("asdas;"); } void debug(float dt) diff --git a/src/light.c b/src/light.c index 58a98e6..d3508b9 100644 --- a/src/light.c +++ b/src/light.c @@ -32,6 +32,12 @@ void light_create(struct Entity* entity, int light_type) light->inner_angle = TO_RADIANS(20.f); light->radius = 20; vec3_fill(&light->color, 1.f, 1.f, 1.f); + light_add(entity); +} + +void light_add(struct Entity* entity) +{ + assert(entity->type == ET_LIGHT); int* new_index = array_grow(light_list, int); *new_index = entity->id; } diff --git a/src/light.h b/src/light.h index 653f341..8aae134 100644 --- a/src/light.h +++ b/src/light.h @@ -9,6 +9,7 @@ void light_init(void); void light_cleanup(void); void light_destroy(struct Entity* entity); void light_create(struct Entity* entity, int light_type); +void light_add(struct Entity* entity); int* light_get_valid_indices(int* out_count); #endif diff --git a/src/material.c b/src/material.c index 991f5a3..93cb993 100644 --- a/src/material.c +++ b/src/material.c @@ -185,17 +185,18 @@ void material_cleanup(void) array_free(empty_indices); } -int material_register_model(struct Model* model, int model_index, const char* material_name) +bool material_register_model(struct Entity* entity, const char* material_name) { - assert(material_name && model); - int success = 0; + assert(material_name && entity); + bool success = false; int index = material_get_index(material_name); if(index < -1) { log_error("material:register_model", "Material '%s' not found", material_name); return success; } - + + struct Model* model = &entity->model; struct Material* material = &material_list[index]; model->material = index; model->material_params = array_new(struct Material_Param); @@ -237,14 +238,16 @@ int material_register_model(struct Model* model, int model_index, const char* ma break; } } - array_push(material->registered_models, model_index, int); - success = 1; + + array_push(material->registered_models, entity->id, int); + success = true; return success; } -void material_unregister_model(struct Model* model, int model_index) +void material_unregister_model(struct Entity* entity) { - assert(model); + assert(entity); + struct Model* model = &entity->model; struct Material* material = &material_list[model->material]; /* Remove textures, if any */ for(int i = 0; i < array_len(model->material_params); i++) @@ -257,7 +260,7 @@ void material_unregister_model(struct Model* model, int model_index) /* Remove model index from material registry*/ for(int i = 0; i < array_len(material->registered_models); i++) { - if(material->registered_models[i] == model_index) + if(material->registered_models[i] == entity->id) { array_remove_at(material->registered_models, i); break; diff --git a/src/material.h b/src/material.h index c980a35..96bf9a9 100644 --- a/src/material.h +++ b/src/material.h @@ -4,7 +4,7 @@ #include "linmath.h" #include "num_types.h" -struct Model; +struct Entity; struct Uniform { @@ -44,8 +44,8 @@ struct Material* material_get(int index); int material_get_index(const char* material_name); void material_init(void); void material_cleanup(void); -int material_register_model(struct Model* model, int model_index, const char* material_name); -void material_unregister_model(struct Model* model, int model_index); +bool material_register_model(struct Entity* entity, const char* material_name); +void material_unregister_model(struct Entity* entity); void material_remove(int index); #endif diff --git a/src/model.c b/src/model.c index 627d0ce..a2cde6c 100644 --- a/src/model.c +++ b/src/model.c @@ -19,7 +19,7 @@ void model_create(struct Entity* entity, const char* geo_name, const char* mater int geo_index = geom_create_from_file(geo_name); model->geometry_index = geo_index; - if(!material_register_model(model, entity->id, material_name ? material_name : "Unshaded")) + if(!material_register_model(entity, material_name ? material_name : "Unshaded")) { log_error("model:create", "Unable to register model with Unshaded material, component not added"); model_destroy(entity); @@ -31,7 +31,7 @@ void model_destroy(struct Entity* entity) struct Model* model = &entity->model; geom_remove(model->geometry_index); model->geometry_index = -1; - material_unregister_model(model, entity->id); + material_unregister_model(entity); /* deallocate all params */ for(int i = 0; i < array_len(model->material_params); i++) free(model->material_params[i].value); diff --git a/src/variant.c b/src/variant.c index 70e7102..32ee016 100644 --- a/src/variant.c +++ b/src/variant.c @@ -15,28 +15,28 @@ void variant_init_empty(struct Variant* variant) variant->val_str = NULL; } -void variant_assign_float(struct Variant* variant, float value) +void variant_assign_float(struct Variant* variant, const float value) { if(variant->type != VT_FLOAT) variant_free(variant); variant->type = VT_FLOAT; variant->val_float = value; } -void variant_assign_int(struct Variant* variant, int value) +void variant_assign_int(struct Variant* variant, const int value) { if(variant->type != VT_INT) variant_free(variant); variant->type = VT_INT; variant->val_int = value; } -void variant_assign_double(struct Variant* variant, double value) +void variant_assign_double(struct Variant* variant, const double value) { if(variant->type != VT_DOUBLE) variant_free(variant); variant->type = VT_DOUBLE; variant->val_double = value; } -void variant_assign_bool(struct Variant* variant, bool value) +void variant_assign_bool(struct Variant* variant, const bool value) { if(variant->type != VT_BOOL) variant_free(variant); variant->type = VT_BOOL; @@ -271,3 +271,22 @@ void variant_from_str(struct Variant* variant, const char* str, int variant_type default: /* Other types not supported, quietly return */ break; } } + +void variant_copy_out(void* to, const struct Variant* from) +{ + switch(from->type) + { + case VT_BOOL: *(bool*)to = from->val_bool; break; + case VT_INT: *(int*)to = from->val_int; break; + case VT_FLOAT: *(float*)to = from->val_float; break; + case VT_DOUBLE: *(double*)to = from->val_double; break; + case VT_VEC2: vec2_assign((vec2*)to, &from->val_vec2); break; + case VT_VEC3: vec3_assign((vec3*)to, &from->val_vec3); break; + case VT_VEC4: vec4_assign((vec4*)to, &from->val_vec4); break; + case VT_QUAT: quat_assign((quat*)to, &from->val_quat); break; + case VT_MAT4: mat4_assign((mat4*)to, from->val_mat4); break; + case VT_STR: strncpy(to, from->val_str, strlen(from->val_str)); break; + default: /* Nothing to be done for the rest*/ + break; + } +} diff --git a/src/variant.h b/src/variant.h index d4bb9b4..f29ca81 100644 --- a/src/variant.h +++ b/src/variant.h @@ -43,10 +43,10 @@ struct Variant }; void variant_init_empty(struct Variant* variant); -void variant_assign_float(struct Variant* variant, float value); -void variant_assign_int(struct Variant* variant, int value); -void variant_assign_double(struct Variant* variant, double value); -void variant_assign_bool(struct Variant* variant, bool value); +void variant_assign_float(struct Variant* variant, const float value); +void variant_assign_int(struct Variant* variant, const int value); +void variant_assign_double(struct Variant* variant, const double value); +void variant_assign_bool(struct Variant* variant, const bool value); void variant_assign_str(struct Variant* variant, const char* value); void variant_assign_vec2(struct Variant* variant, const vec2* value); void variant_assign_vec3(struct Variant* variant, const vec3* value); @@ -59,6 +59,7 @@ void variant_assign_quatf(struct Variant* variant, const float x, const float y, void variant_assign_mat4(struct Variant* variant, const mat4* source); void variant_assign_ptr(struct Variant* variant, void* source); void variant_copy(struct Variant* to, const struct Variant* from); +void variant_copy_out(void* to, const struct Variant* from); /* In case of VT_STR the to variable must already be preallocated otherwise this will crash in glorious ways */ void variant_free(struct Variant* variant); void variant_to_str(const struct Variant* variant, char* str, int len); void variant_from_str(struct Variant* variant, const char* str, int variant_type);