diff options
Diffstat (limited to 'save.c')
| -rw-r--r-- | save.c | 1056 |
1 files changed, 1056 insertions, 0 deletions
@@ -0,0 +1,1056 @@ +#include "save.h" +#include "ent.h" + +typedef struct { + i64 size_pos; + i64 data_pos; +} SaveChunk; + +typedef struct { + u64 next_pid; +} SaveLoadContext; + +static bool +read_u8_le(FILE *fp, u8 *val) { + return fread(val, 1, 1, fp) == 1; +} + +static bool +write_u8_le(FILE *fp, u8 val) { + return fwrite(&val, 1, 1, fp) == 1; +} + +static bool +read_u16_le(FILE *fp, u16 *val) { + u8 bytes[2]; + + if (fread(bytes, 1, 2, fp) != 2) { + return false; + } + + *val = (u16)bytes[0] + | (u16)bytes[1] << 8; + + return true; +} + +static bool +write_u16_le(FILE *fp, u16 val) { + u8 bytes[2] = { + (u8)(val >> 0), + (u8)(val >> 8), + }; + + return fwrite(bytes, 1, 2, fp) == 2; +} + +static bool +read_u32_le(FILE *fp, u32 *val) { + u8 bytes[4]; + + if (fread(bytes, 1, 4, fp) != 4) { + return false; + } + + *val = (u32)bytes[0] << 0 + | (u32)bytes[1] << 8 + | (u32)bytes[2] << 16 + | (u32)bytes[3] << 24; + + return true; +} + +static bool +write_u32_le(FILE *fp, u32 val) { + u8 bytes[4] = { + (u8)(val >> 0), + (u8)(val >> 8), + (u8)(val >> 16), + (u8)(val >> 24), + }; + + return fwrite(bytes, 1, 4, fp) == 4; +} + +static bool +read_u64_le(FILE *fp, u64 *val) { + u8 bytes[8]; + + if (fread(bytes, 1, 8, fp) != 8) { + return false; + } + + *val = (u64)bytes[0] << 0 + | (u64)bytes[1] << 8 + | (u64)bytes[2] << 16 + | (u64)bytes[3] << 24 + | (u64)bytes[4] << 32 + | (u64)bytes[5] << 40 + | (u64)bytes[6] << 48 + | (u64)bytes[7] << 56; + + return true; +} + +static bool +write_u64_le(FILE *fp, u64 val) { + u8 bytes[8] = { + (u8)(val >> 0), + (u8)(val >> 8), + (u8)(val >> 16), + (u8)(val >> 24), + (u8)(val >> 32), + (u8)(val >> 40), + (u8)(val >> 48), + (u8)(val >> 56), + }; + + return fwrite(bytes, 1, 8, fp) == 8; +} + +static bool +read_f32_le(FILE *fp, f32 *val) { + u32 bits; + if (!read_u32_le(fp, &bits)) { + return false; + } + + memcpy(val, &bits, sizeof(*val)); + return true; +} + +static bool +write_f32_le(FILE *fp, f32 value) { + u32 bits; + memcpy(&bits, &value, 4); + return write_u32_le(fp, bits); +} + +static bool +read_vec3(FILE *fp, Vec3 *val) { + return read_f32_le(fp, &val->x) && + read_f32_le(fp, &val->y) && + read_f32_le(fp, &val->z); +} + +static bool +write_vec3(FILE *fp, Vec3 val) { + return write_f32_le(fp, val.x) && + write_f32_le(fp, val.y) && + write_f32_le(fp, val.z); +} + +static bool +read_quat(FILE *fp, Quaternion *val) { + return read_f32_le(fp, &val->x) && + read_f32_le(fp, &val->y) && + read_f32_le(fp, &val->z) && + read_f32_le(fp, &val->w); +} + +static bool +write_quat(FILE *fp, Quaternion val) { + return write_f32_le(fp, val.x) && + write_f32_le(fp, val.y) && + write_f32_le(fp, val.z) && + write_f32_le(fp, val.w); +} + +static bool +write_byte_arr(FILE *fp, const void *data, u32 len) { + return fwrite(data, 1, len, fp) == len; +} + +/* some entities are 'transient', so we need a persistent entity count */ +static u32 +persistent_entity_count(void) { + u32 cnt = 0; + + for (u32 i = 0; i < entity_count(); ++i) { + if (entity_data()[i].pid != 0) { + cnt++; + } + } + + return cnt; +} + +static bool +chunk_begin(FILE *fp, const char label[4], SaveChunk *chunk) { + if (!write_byte_arr(fp, label, 4)) { + return false; + } + + chunk->size_pos = ftell(fp); + if (chunk->size_pos < 0 || !write_u32_le(fp, 0)) { + return false; + } + + chunk->data_pos = ftell(fp); + return chunk->data_pos >= 0; +} + +static bool +chunk_end(FILE *fp, SaveChunk *chunk) { + u32 end_pos = ftell(fp); + if (end_pos < chunk->data_pos) { + return false; + } + + u64 size = (u64)(end_pos - chunk->data_pos); + if (size > UINT32_MAX) { + return false; + } + + if (fseek(fp, chunk->size_pos, SEEK_SET) != 0 || + !write_u32_le(fp, (u32)size) || + fseek(fp, end_pos, SEEK_SET) != 0) { + return false; + } + + return true; +} + +static bool +read_header(FILE *fp) { + char magic[4]; + u16 major, minor; + + if (fread(magic, 1, 4, fp) != 4 || + !read_u16_le(fp, &major) || + !read_u16_le(fp, &minor)) { + return false; + } + + if (memcmp(magic, "FGSF", 4) != 0) { + TraceLog(LOG_ERROR, "invalid save file magic."); + return false; + } + + if (major != SAVE_VERSION_MAJOR || + minor != SAVE_VERSION_MINOR) { + TraceLog(LOG_ERROR, "unsupported save file version %u.%u.", + major, minor); + return false; + } + + return true; +} + +static bool +write_header(FILE *fp) { + return fwrite("FGSF", 1, 4, fp) == 4 && + write_u16_le(fp, SAVE_VERSION_MAJOR) && + write_u16_le(fp, SAVE_VERSION_MINOR); +} + +static bool +read_entity_chunk(FILE *fp, SaveLoadContext *ctx) { + u64 next_pid; + u32 entity_cnt; + + if (!read_u64_le(fp, &next_pid) || + !read_u32_le(fp, &entity_cnt)) { + return false; + } + + if (next_pid == 0) { + return false; + } + + ctx->next_pid = next_pid; + + for (u32 i = 0; i < entity_cnt; ++i) { + u64 pid = 0; + read_u64_le(fp, &pid); + + if (pid == 0 || pid >= next_pid) { + TraceLog(LOG_ERROR, "entity PID invalid."); + return false; + } + + u32 name_len = 0; + read_u32_le(fp, &name_len); + + u64 id = entity_restore(pid); + if (!id) { + TraceLog(LOG_ERROR, "entity pid already exists."); + return false; + } + Entity *ent = entity_get(id); + ent->name = malloc(name_len + 1); + + if (name_len > 0 && + fread(ent->name, 1, name_len, fp) != name_len) { + TraceLog(LOG_ERROR, "failed to read entity name."); + return false; + } + + ent->name[name_len] = '\0'; + } + + entity_system_set_next_pid(next_pid); + return true; +} + +static bool +write_entity_chunk(FILE *fp) { + SaveChunk chunk = {0}; + + if (!chunk_begin(fp, "ENTT", &chunk) || + !write_u64_le(fp, entity_next_pid()) || + !write_u32_le(fp, persistent_entity_count())) { + return false; + } + + for (u32 i = 0; i < entity_count(); ++i) { + Entity *ent = &entity_data()[i]; + + if (ent->pid == 0) { + continue; + } + + if (!write_u64_le(fp, ent->pid) || + !write_u32_le(fp, strlen(ent->name)) || + !write_byte_arr(fp, ent->name, strlen(ent->name))) { + return false; + } + } + + return chunk_end(fp, &chunk); +} + +static bool +read_transform_chunk(FILE *fp, SaveLoadContext *ctx) { + u32 tr_cnt; + read_u32_le(fp, &tr_cnt); + + u64 *parent_pids = calloc(ctx->next_pid, sizeof(u64)); + if (!parent_pids) { + TraceLog(LOG_FATAL, "calloc failed."); + } + + for (u32 i = 0; i < tr_cnt; ++i) { + u64 pid, parent_pid; + + if (!read_u64_le(fp, &pid) || + !read_u64_le(fp, &parent_pid) || + pid == 0 || pid >= ctx->next_pid || + parent_pid >= ctx->next_pid) { + goto fail; + } + + u64 id = entity_id_from_pid(pid); + Entity *ent = entity_get(id); + if (!ent) { + TraceLog(LOG_ERROR, "failed to properly load entity"); + goto fail; + } + + if (!read_vec3(fp, &ent->tr.pos) || + !read_quat(fp, &ent->tr.rot) || + !read_vec3(fp, &ent->tr.scale)) { + goto fail; + } + + parent_pids[pid] = parent_pid; + } + + /* Build hierarchy only after all transforms exist. */ + for (u64 pid = 1; pid < ctx->next_pid; ++pid) { + u64 parent_pid = parent_pids[pid]; + + if (!parent_pid) { + continue; + } + + u64 id = entity_id_from_pid(pid); + u64 parent_id = entity_id_from_pid(parent_pid); + + if (!id || !parent_id || !entity_set_parent(id, parent_id)) { + TraceLog(LOG_ERROR, "entity hierarchy build failed."); + goto fail; + } + } + + free(parent_pids); + return true; + +fail: + free(parent_pids); + return false; +} + +static bool +write_transform_chunk(FILE *fp) { + SaveChunk chunk; + + u32 tr_cnt = 0; + for (u32 i = 0; i < entity_count(); ++i) { + if (entity_data()[i].pid) { + tr_cnt++; + } + } + + if (!chunk_begin(fp, "TRFM", &chunk) || + !write_u32_le(fp, tr_cnt)) { + return false; + } + + for (u32 i = 0; i < entity_count(); ++i) { + Entity *ent = &entity_data()[i]; + + if (!ent->pid) { + continue; + } + + u64 parent_pid = 0; + if (ent->tr.parent) { + Entity *parent = entity_get(ent->tr.parent); + if (!parent) { + return false; + } + parent_pid = parent->pid; + } + + if (!write_u64_le(fp, ent->pid) || + !write_u64_le(fp, parent_pid) || + !write_vec3(fp, ent->tr.pos) || + !write_quat(fp, ent->tr.rot) || + !write_vec3(fp, ent->tr.scale)) { + return false; + } + } + + return chunk_end(fp, &chunk); +} + +static bool +read_model_chunk(FILE *fp, SaveLoadContext *ctx) { + u32 mdl_cnt; + if (!read_u32_le(fp, &mdl_cnt)) { + return false; + } + + for (u32 i = 0; i < mdl_cnt; ++i) { + u64 pid; + u32 key_len; + u8 draw; + + if (!read_u64_le(fp, &pid) || !read_u32_le(fp, &key_len)) { + return false; + } + + if (pid == 0 || pid >= ctx->next_pid) { + return false; + } + + u64 id = entity_id_from_pid(pid); + Entity *ent = entity_get(id); + + if (!ent || ent->mdl.has) { + return false; + } + + char *key = malloc(key_len + 1); + if (!key) { + return false; + } + + if (key_len && fread(key, 1, key_len, fp) != key_len) { + free(key); + return false; + } + + key[key_len] = '\0'; + + if (!read_u8_le(fp, &draw)) { + free(key); + return false; + } + + ent->mdl.key = key; + ent->mdl.draw = draw; + ent->mdl.has = true; + } + + return true; +} + +static bool +write_model_chunk(FILE *fp) { + SaveChunk chunk = {0}; + u32 mdl_cnt = 0; + + for (u32 i = 0; i < entity_count(); ++i) { + Entity *ent = &entity_data()[i]; + + if (ent->pid && ent->mdl.has) { + mdl_cnt++; + } + } + + if (!chunk_begin(fp, "MODL", &chunk) || + !write_u32_le(fp, mdl_cnt)) { + return false; + } + + for (u32 i = 0; i < entity_count(); ++i) { + Entity *ent = &entity_data()[i]; + + if (!ent->pid || !ent->mdl.has) { + continue; + } + + u32 key_len = (u32)strlen(ent->mdl.key); + + if (!write_u64_le(fp, ent->pid) || + !write_u32_le(fp, key_len) || + !write_byte_arr(fp, ent->mdl.key, key_len) || + !write_u8_le(fp, ent->mdl.draw ? 1 : 0)) { + return false; + } + } + + return chunk_end(fp, &chunk); +} + +static bool +read_physics_shape(FILE *fp, PhysicsShape *shape) { + *shape = (PhysicsShape){0}; + + u8 tag = 0; + if (!read_u8_le(fp, &tag)) { + TraceLog(LOG_ERROR, "failed to read physics shape tag"); + return false; + } + shape->tag = tag; + + if (!read_vec3(fp, &shape->pos) || + !read_quat(fp, &shape->rot) || + !read_f32_le(fp, &shape->density) || + !read_f32_le(fp, &shape->friction) || + !read_f32_le(fp, &shape->restitution) || + !read_u32_le(fp, &shape->category_bits) || + !read_u32_le(fp, &shape->mask_bits)) { + TraceLog(LOG_ERROR, "failed to read physics shape."); + return false; + } + + switch (shape->tag) { + case PHYS_SHAPE_BOX: + return read_vec3(fp, &shape->Box.sz); + break; + + case PHYS_SHAPE_SPHERE: + return read_f32_le(fp, &shape->Sphere.radius); + break; + + case PHYS_SHAPE_CAPSULE: + return read_f32_le(fp, &shape->Capsule.height) && + read_f32_le(fp, &shape->Capsule.radius); + break; + + case PHYS_SHAPE_CYLINDER: + return read_f32_le(fp, &shape->Cylinder.height) && + read_f32_le(fp, &shape->Cylinder.radius); + break; + + case PHYS_SHAPE_CONVEX_HULL: + if (!read_u32_le(fp, &shape->ConvexHull.point_cnt)) { + return false; + } + + if (shape->ConvexHull.point_cnt) { + shape->ConvexHull.points = + calloc(shape->ConvexHull.point_cnt, + sizeof(Vec3)); + + if (!shape->ConvexHull.points) { + return false; + } + } + + for (u32 i = 0; i < shape->ConvexHull.point_cnt; ++i) { + if (!read_vec3(fp, &shape->ConvexHull.points[i])) { + return false; + } + } + break; + + case PHYS_SHAPE_STATIC_TRIMESH: + if (!read_u32_le(fp, &shape->TriMesh.point_cnt) || + !read_u32_le(fp, &shape->TriMesh.idx_cnt)) { + return false; + } + + if (shape->TriMesh.point_cnt) { + shape->TriMesh.points = + calloc(shape->TriMesh.point_cnt, + sizeof(Vec3)); + + if (!shape->TriMesh.points) { + return false; + } + } + + if (shape->TriMesh.idx_cnt) { + shape->TriMesh.indices = + calloc(shape->TriMesh.idx_cnt, sizeof(u32)); + + if (!shape->TriMesh.indices) { + return false; + } + } + + for (u32 i = 0; i < shape->TriMesh.point_cnt; ++i) { + if (!read_vec3(fp, &shape->TriMesh.points[i])) { + return false; + } + } + + for (u32 i = 0; i < shape->TriMesh.idx_cnt; ++i) { + if (!read_u32_le(fp, &shape->TriMesh.indices[i])) { + return false; + } + } + break; + }; + + return true; +} + +static bool +write_physics_shape(FILE *fp, PhysicsShape *shape) { + if (!write_u8_le(fp, (u8)shape->tag) || + !write_vec3(fp, shape->pos) || + !write_quat(fp, shape->rot) || + !write_f32_le(fp, shape->density) || + !write_f32_le(fp, shape->friction) || + !write_f32_le(fp, shape->restitution) || + !write_u32_le(fp, shape->category_bits) || + !write_u32_le(fp, shape->mask_bits)) { + return false; + } + + switch (shape->tag) { + case PHYS_SHAPE_BOX: + return write_vec3(fp, shape->Box.sz); + break; + + case PHYS_SHAPE_SPHERE: + return write_f32_le(fp, shape->Sphere.radius); + break; + + case PHYS_SHAPE_CAPSULE: + return write_f32_le(fp, shape->Capsule.height) && + write_f32_le(fp, shape->Capsule.radius); + break; + + case PHYS_SHAPE_CYLINDER: + return write_f32_le(fp, shape->Cylinder.height) && + write_f32_le(fp, shape->Cylinder.radius); + break; + + case PHYS_SHAPE_CONVEX_HULL: + if (shape->ConvexHull.point_cnt && + !shape->ConvexHull.points) { + return false; + } + + if (!write_u32_le(fp, shape->ConvexHull.point_cnt)) { + return false; + } + + for (u32 i = 0; i < shape->ConvexHull.point_cnt; ++i) { + if (!write_vec3(fp, shape->ConvexHull.points[i])) { + return false; + } + } + break; + + case PHYS_SHAPE_STATIC_TRIMESH: + if ((shape->TriMesh.point_cnt && !shape->TriMesh.points) || + (shape->TriMesh.idx_cnt && !shape->TriMesh.indices)) { + return false; + } + + if (!write_u32_le(fp, shape->TriMesh.point_cnt) || + !write_u32_le(fp, shape->TriMesh.idx_cnt)) { + return false; + } + + for (u32 i = 0; i < shape->TriMesh.point_cnt; ++i) { + if (!write_vec3(fp, shape->TriMesh.points[i])) { + return false; + } + } + + for (u32 i = 0; i < shape->TriMesh.idx_cnt; ++i) { + if (!write_u32_le(fp, shape->TriMesh.indices[i])) { + return false; + } + } + break; + } + + return true; +} + +static bool +read_physics_body(FILE *fp, PhysicsBody *body) { + u8 mode, enable_sleep; + *body = (PhysicsBody){0}; + + if (!read_vec3(fp, &body->pos) || + !read_quat(fp, &body->rot) || + !read_f32_le(fp, &body->linear_damping) || + !read_f32_le(fp, &body->angular_damping) || + !read_f32_le(fp, &body->gravity_scale) || + !read_u8_le(fp, &mode) || + !read_u8_le(fp, &enable_sleep) || + !read_u32_le(fp, &body->shape_cnt)) { + return false; + } + + body->mode = (PhysicsMode)mode; + body->enable_sleep = enable_sleep; + + if (body->shape_cnt) { + body->shapes = calloc(body->shape_cnt, sizeof(PhysicsShape)); + if (!body->shapes) { + return false; + } + } + + for (u32 i = 0; i < body->shape_cnt; ++i) { + if (!read_physics_shape(fp, &body->shapes[i])) { + return false; + } + } + + return true; +} + +static bool +write_physics_body(FILE *fp, PhysicsBody *body) { + if (!write_vec3(fp, body->pos) || + !write_quat(fp, body->rot) || + !write_f32_le(fp, body->linear_damping) || + !write_f32_le(fp, body->angular_damping) || + !write_f32_le(fp, body->gravity_scale) || + !write_u8_le(fp, (u8)body->mode) || + !write_u8_le(fp, body->enable_sleep ? 1 : 0) || + !write_u32_le(fp, body->shape_cnt)) { + return false; + } + + if (body->shape_cnt && !body->shapes) { + return false; + } + + for (u32 i = 0; i < body->shape_cnt; ++i) { + if (!write_physics_shape(fp, &body->shapes[i])) { + return false; + } + } + + return true; +} + +static bool +read_physics_joint(FILE *fp, PhysicsJoint *joint, u32 body_cnt) { + u8 tag; + *joint = (PhysicsJoint){0}; + + if (!read_u8_le(fp, &tag) || + tag > PHYS_JOINT_DISTANCE || + !read_u32_le(fp, &joint->body_a) || + !read_u32_le(fp, &joint->body_b) || + joint->body_a >= body_cnt || + joint->body_b >= body_cnt || + !read_u32_le(fp, &joint->flags) || + !read_vec3(fp, &joint->anchor_a) || + !read_vec3(fp, &joint->anchor_b) || + !read_vec3(fp, &joint->axis_a) || + !read_vec3(fp, &joint->axis_b) || + !read_f32_le(fp, &joint->lower) || + !read_f32_le(fp, &joint->upper) || + !read_f32_le(fp, &joint->spring_hz) || + !read_f32_le(fp, &joint->spring_damping) || + !read_f32_le(fp, &joint->motor_speed) || + !read_f32_le(fp, &joint->motor_force)) { + return false; + } + + joint->tag = (PhysicsJointTag)tag; + return true; +} + +static bool +write_physics_joint(FILE *fp, PhysicsJoint *joint, u32 body_cnt) { + if (joint->body_a >= body_cnt || + joint->body_b >= body_cnt) { + return false; + } + + return write_u8_le(fp, (u8)joint->tag) && + write_u32_le(fp, joint->body_a) && + write_u32_le(fp, joint->body_b) && + write_u32_le(fp, joint->flags) && + write_vec3(fp, joint->anchor_a) && + write_vec3(fp, joint->anchor_b) && + write_vec3(fp, joint->axis_a) && + write_vec3(fp, joint->axis_b) && + write_f32_le(fp, joint->lower) && + write_f32_le(fp, joint->upper) && + write_f32_le(fp, joint->spring_hz) && + write_f32_le(fp, joint->spring_damping) && + write_f32_le(fp, joint->motor_speed) && + write_f32_le(fp, joint->motor_force); +} + +static bool +read_physics_chunk(FILE *fp, SaveLoadContext *ctx) { + u32 phys_cnt = 0; + if (!read_u32_le(fp, &phys_cnt)) { + return false; + } + + for (u32 i = 0; i < phys_cnt; ++i) { + u64 pid; + u32 body_cnt, joint_cnt; + + if (!read_u64_le(fp, &pid) || !pid || pid >= ctx->next_pid) { + return false; + } + + u64 id = entity_id_from_pid(pid); + Entity *ent = entity_get(id); + + if (!read_u32_le(fp, &body_cnt) || !ent || ent->phys.has) { + return false; + } + + ent->phys.body_cnt = body_cnt; + + if (body_cnt) { + ent->phys.bodies = + calloc(body_cnt, sizeof(PhysicsBody)); + if (!ent->phys.bodies) { + return false; + } + } + + for (u32 j = 0; j < body_cnt; ++j) { + if (!read_physics_body(fp, &ent->phys.bodies[j])) { + return false; + } + } + + if (!read_u32_le(fp, &joint_cnt)) { + return false; + } + + ent->phys.joint_cnt = joint_cnt; + + if (joint_cnt) { + ent->phys.joints = + calloc(joint_cnt, sizeof(PhysicsJoint)); + if (!ent->phys.joints) { + return false; + } + } + + for (u32 j = 0; j < joint_cnt; ++j) { + if (!read_physics_joint(fp, + &ent->phys.joints[j], body_cnt)) { + return false; + } + } + + ent->phys.has = true; + } + + return true; +} + +static bool +write_physics_chunk(FILE *fp) { + SaveChunk chunk = {0}; + u32 phys_cnt = 0; + + for (u32 i = 0; i < entity_count(); ++i) { + Entity *ent = &entity_data()[i]; + + if (ent->pid && ent->phys.has) { + phys_cnt++; + } + } + + if (!chunk_begin(fp, "PHYS", &chunk) || + !write_u32_le(fp, phys_cnt)) { + return false; + } + + for (u32 i = 0; i < entity_count(); ++i) { + Entity *ent = &entity_data()[i]; + PhysicsCmp *phys = &ent->phys; + + if (!ent->pid || !phys->has) { + continue; + } + + if ((phys->body_cnt && !phys->bodies) || + (phys->joint_cnt && !phys->joints) || + !write_u64_le(fp, ent->pid) || + !write_u32_le(fp, phys->body_cnt)) { + return false; + } + + for (u32 j = 0; j < phys->body_cnt; ++j) { + if (!write_physics_body(fp, &phys->bodies[j])) { + return false; + } + } + + if (!write_u32_le(fp, phys->joint_cnt)) { + return false; + } + + for (u32 j = 0; j < phys->joint_cnt; ++j) { + if (!write_physics_joint(fp, + &phys->joints[j], phys->body_cnt)) { + return false; + } + } + } + + return chunk_end(fp, &chunk); +} + +bool +save_file_read(cstr path) { + FILE *fp = fopen(path, "rb"); + if (!fp) { + TraceLog(LOG_ERROR, "save file '%s' failed to load.", path); + return false; + } + + SaveLoadContext ctx = {0}; + bool success = read_header(fp); + bool read_entt = false; + + while (success) { + char label[4]; + size_t read_cnt = fread(label, 1, 4, fp); + if (read_cnt == 0 && feof(fp)) { + break; + } + + if (read_cnt != 4) { + TraceLog(LOG_ERROR, "failed to read chunk label."); + success = false; + break; + } + + u32 chunk_size; + if (!read_u32_le(fp, &chunk_size)) { + TraceLog(LOG_ERROR, "failed to read chunk size."); + success = false; + break; + } + + u32 data_pos = ftell(fp); + if (data_pos < 0) { + success = false; + break; + } + + u32 end_pos = data_pos + chunk_size; + + if (memcmp(label, "ENTT", 4) == 0) { + if (read_entt) { + TraceLog(LOG_ERROR, "duplicate ENTT chunk."); + success = false; + break; + } + + success = read_entity_chunk(fp, &ctx); + read_entt = success; + } else if (memcmp(label, "TRFM", 4) == 0) { + if (!read_entt) { + TraceLog(LOG_ERROR, "TRFM found before ENTT."); + success = false; + break; + } + success = read_transform_chunk(fp, &ctx); + } else if (memcmp(label, "MODL", 4) == 0) { + if (!read_entt) { + TraceLog(LOG_ERROR, "TRFM found before ENTT."); + success = false; + break; + } + success = read_model_chunk(fp, &ctx); + } else if (memcmp(label, "PHYS", 4) == 0) { + if (!read_entt) { + TraceLog(LOG_ERROR, "PHYS found before ENTT."); + success = false; + break; + } + success = read_physics_chunk(fp, &ctx); + } else { + TraceLog(LOG_INFO, "skipped unknown chunk."); + } + + if (!success) { + break; + } + + u32 pos = ftell(fp); + if (pos < 0 || pos > end_pos) { + TraceLog(LOG_ERROR, + "chunk reader exceeded chunk size."); + success = false; + break; + } + + if (fseek(fp, end_pos, SEEK_SET) != 0) { + success = false; + break; + } + } + + if (!read_entt) { + TraceLog(LOG_ERROR, "save contains no ENTT chunk."); + success = false; + } + + if (fclose(fp) != 0) { + success = false; + } + + return success; +} + +bool +save_file_write(cstr path) { + FILE *fp = fopen(path, "wb"); + if (!fp) { + return false; + } + + bool success = + write_header(fp) && + write_entity_chunk(fp) && + write_transform_chunk(fp) && + write_model_chunk(fp) && + write_physics_chunk(fp); + + if (fclose(fp) != 0) { + success = false; + } + + return success; +} + |
