#include "config_loader.h" #include #include #include #include "simulation.h" static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft); static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result); static bool extract_vec3_from_table(toml_datum_t table, Vec3* pos) { toml_datum_t x = toml_get(table, "x"); toml_datum_t y = toml_get(table, "y"); toml_datum_t z = toml_get(table, "z"); // Accept both INT64 and FP64 for coordinates (TOML may parse integers as floats) if ((x.type != TOML_FP64 && x.type != TOML_INT64) || (y.type != TOML_FP64 && y.type != TOML_INT64) || (z.type != TOML_FP64 && z.type != TOML_INT64)) { return false; } pos->x = x.type == TOML_FP64 ? x.u.fp64 : (double)x.u.int64; pos->y = y.type == TOML_FP64 ? y.u.fp64 : (double)y.u.int64; pos->z = z.type == TOML_FP64 ? z.u.fp64 : (double)z.u.int64; return true; } static bool extract_color_from_table(toml_datum_t table, float* color) { toml_datum_t r = toml_get(table, "r"); toml_datum_t g = toml_get(table, "g"); toml_datum_t b = toml_get(table, "b"); // Accept both INT64 and FP64 for color components if ((r.type != TOML_FP64 && r.type != TOML_INT64) || (g.type != TOML_FP64 && g.type != TOML_INT64) || (b.type != TOML_FP64 && b.type != TOML_INT64)) { return false; } color[0] = (float)(r.type == TOML_FP64 ? r.u.fp64 : (double)r.u.int64); color[1] = (float)(g.type == TOML_FP64 ? g.u.fp64 : (double)g.u.int64); color[2] = (float)(b.type == TOML_FP64 ? b.u.fp64 : (double)b.u.int64); return true; } static bool parse_toml_body(toml_datum_t body_table, CelestialBody* body) { // Extract string fields toml_datum_t name = toml_get(body_table, "name"); if (name.type != TOML_STRING) { return false; } strncpy(body->name, name.u.s, 63); body->name[63] = '\0'; // Extract numeric fields toml_datum_t mass = toml_get(body_table, "mass"); toml_datum_t radius = toml_get(body_table, "radius"); toml_datum_t parent_idx = toml_get(body_table, "parent_index"); toml_datum_t eccentricity = toml_get(body_table, "eccentricity"); toml_datum_t semi_major = toml_get(body_table, "semi_major_axis"); if (mass.type != TOML_FP64 || radius.type != TOML_FP64 || parent_idx.type != TOML_INT64 || eccentricity.type != TOML_FP64 || semi_major.type != TOML_FP64) { return false; } body->mass = mass.u.fp64; body->radius = radius.u.fp64; body->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64); body->eccentricity = eccentricity.u.fp64; body->semi_major_axis = semi_major.u.fp64; // Extract position vector toml_datum_t position = toml_get(body_table, "position"); if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &body->position)) { return false; } // Extract color toml_datum_t color = toml_get(body_table, "color"); if (color.type != TOML_TABLE || !extract_color_from_table(color, body->color)) { return false; } // Initialize velocity (will be calculated later) body->velocity = {0.0, 0.0, 0.0}; body->soi_radius = 0.0; return true; } bool load_system_config(SimulationState* sim, const char* filepath) { toml_result_t result = toml_parse_file_ex(filepath); if (!result.ok) { printf("Error: Could not parse TOML config file: %s\n", filepath); printf("TOML Error: %s\n", result.errmsg); return false; } // Get bodies array toml_datum_t bodies = toml_get(result.toptab, "bodies"); if (bodies.type != TOML_ARRAY) { printf("Error: Expected 'bodies' array in config file: %s\n", filepath); toml_free(result); return false; } // Count bodies first int body_count = bodies.u.arr.size; if (body_count == 0) { printf("Error: No bodies found in config file: %s\n", filepath); toml_free(result); return false; } if (body_count > sim->max_bodies) { printf("Error: Too many bodies (%d) for simulation (max: %d)\n", body_count, sim->max_bodies); toml_free(result); return false; } for (int i = 0; i < body_count; i++) { toml_datum_t body_table = bodies.u.arr.elem[i]; if (!parse_toml_body(body_table, &sim->bodies[i])) { printf("Error: Failed to parse body at index %d\n", i); toml_free(result); return false; } if (sim->bodies[i].parent_index != -1 && sim->bodies[i].parent_index >= i) { printf("Error: Body '%s' (index %d) has invalid parent_index %d - must be < %d or -1\n", sim->bodies[i].name, i, sim->bodies[i].parent_index, i); toml_free(result); return false; } } // Validate parent-child distances for (int i = 0; i < body_count; i++) { if (sim->bodies[i].parent_index >= 0) { CelestialBody* body = &sim->bodies[i]; CelestialBody* parent = &sim->bodies[body->parent_index]; double distance = vec3_distance(body->position, parent->position); double min_distance = parent->radius + body->radius; if (distance < min_distance) { printf("Error: Body '%s' (index %d) too close to parent '%s' (index %d)\n", body->name, i, parent->name, body->parent_index); printf(" Distance: %.2e m\n", distance); printf(" Minimum required: %.2e m (parent radius + body radius)\n", min_distance); toml_free(result); return false; } } } sim->body_count = body_count; strncpy(sim->config_name, filepath, sizeof(sim->config_name) - 1); sim->config_name[sizeof(sim->config_name) - 1] = '\0'; // Load spacecraft from the same config file (before freeing TOML data) if (!load_spacecraft_from_toml(sim, result)) { toml_free(result); return false; } toml_free(result); initialize_bodies(sim); printf("Loaded %d bodies from %s\n", body_count, filepath); return true; } static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result) { toml_datum_t spacecraft = toml_get(result.toptab, "spacecraft"); if (spacecraft.type != TOML_ARRAY) { printf("No spacecraft array found in config file: %s (optional)\n", sim->config_name); return true; } int craft_count = spacecraft.u.arr.size; if (craft_count == 0) { printf("No spacecraft found in config file: %s\n", sim->config_name); return true; } if (craft_count > sim->max_craft) { printf("Error: Too many spacecraft (%d) for simulation (max: %d)\n", craft_count, sim->max_craft); return false; } for (int i = 0; i < craft_count; i++) { Spacecraft craft; toml_datum_t craft_table = spacecraft.u.arr.elem[i]; if (!parse_toml_spacecraft(craft_table, &craft)) { printf("Error: Failed to parse spacecraft at index %d\n", i); return false; } if (craft.parent_index < 0 || craft.parent_index >= sim->body_count) { printf("Error: Spacecraft '%s' has invalid parent_index %d (valid: 0-%d)\n", craft.name, craft.parent_index, sim->body_count - 1); return false; } CelestialBody* parent = &sim->bodies[craft.parent_index]; craft.position = vec3_add(parent->position, craft.local_position); craft.velocity = vec3_add(parent->velocity, craft.local_velocity); int idx = add_spacecraft(sim, &craft); if (idx < 0) { printf("Error: Failed to add spacecraft to simulation\n"); return false; } } printf("Loaded %d spacecraft from %s\n", craft_count, sim->config_name); return true; } static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft) { toml_datum_t name = toml_get(craft_table, "name"); if (name.type != TOML_STRING) { return false; } strncpy(craft->name, name.u.s, 63); craft->name[63] = '\0'; toml_datum_t mass = toml_get(craft_table, "mass"); toml_datum_t parent_idx = toml_get(craft_table, "parent_index"); if (mass.type != TOML_FP64 || parent_idx.type != TOML_INT64) { return false; } craft->mass = mass.u.fp64; craft->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64); toml_datum_t position = toml_get(craft_table, "position"); if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &craft->local_position)) { return false; } craft->position = craft->local_position; toml_datum_t velocity = toml_get(craft_table, "velocity"); if (velocity.type == TOML_TABLE) { if (!extract_vec3_from_table(velocity, &craft->local_velocity)) { return false; } } else { craft->local_velocity = {0.0, 0.0, 0.0}; } craft->velocity = craft->local_velocity; return true; }