Object *objects_load(char *name, texture_list_t tl) {
    uint32_t length = 0;
    uint8_t *bytes = platform_load_asset(name, &length);
    if (!bytes) {
        die("Failed to load file %s\n", name);
    }
    printf("load: %s\n", name);
    Object *objectList = mem_mark();
    Object *prevObject = NULL;
    uint32_t p = 0;
    while (p < length) {
        Object *object = mem_bump(sizeof(Object));
        if (prevObject) {
            prevObject->next = object;
        }
        prevObject = object;
        for (int i = 0; i < 16; i++) {
            object->name = get_i8(bytes, &p);
        }
        
        object->mat = mat4_identity();
        object->vertices_len = get_i16(bytes, &p); p += 2;
        object->vertices = NULL; get_i32(bytes, &p);
        object->normals_len = get_i16(bytes, &p); p += 2;
        object->normals = NULL; get_i32(bytes, &p);
        object->primitives_len = get_i16(bytes, &p); p += 2;
        object->primitives = NULL; get_i32(bytes, &p);
        get_i32(bytes, &p);
        get_i32(bytes, &p);
        get_i32(bytes, &p); // Skeleton ref
        object->extent = get_i32(bytes, &p);
        object->flags = get_i16(bytes, &p); p += 2;
        object->next = NULL; get_i32(bytes, &p);
        p += 3 * 3 * 2; // relative rot matrix
        p += 2; // padding
        object->origin.x = get_i32(bytes, &p);
        object->origin.y = get_i32(bytes, &p);
        object->origin.z = get_i32(bytes, &p);
        p += 3 * 3 * 2; // absolute rot matrix
        p += 2; // padding
        p += 3 * 4; // absolute translation matrix
        p += 2; // skeleton update flag
        p += 2; // padding
        p += 4; // skeleton super
        p += 4; // skeleton sub
        p += 4; // skeleton next
        object->radius = 0;
        object->vertices = mem_bump(object->vertices_len * sizeof(vec3_t));
        for (int i = 0; i < object->vertices_len; i++) {
            object->vertices.x = get_i16(bytes, &p);
            object->vertices.y = get_i16(bytes, &p);
            object->vertices.z = get_i16(bytes, &p);
            p += 2; // padding
            if (fabsf(object->vertices.x) > object->radius) {
                object->radius = fabsf(object->vertices.x);
            }
            if (fabsf(object->vertices.y) > object->radius) {
                object->radius = fabsf(object->vertices.y);
            }
            if (fabsf(object->vertices.z) > object->radius) {
                object->radius = fabsf(object->vertices.z);
            }
        }
        object->normals = mem_bump(object->normals_len * sizeof(vec3_t));
        for (int i = 0; i < object->normals_len; i++) {
            object->normals.x = get_i16(bytes, &p);
            object->normals.y = get_i16(bytes, &p);
            object->normals.z = get_i16(bytes, &p);
            p += 2; // padding
        }
        object->primitives = mem_mark();
        for (int i = 0; i < object->primitives_len; i++) {
            Prm prm;
            int16_t prm_type = get_i16(bytes, &p);
            int16_t prm_flag = get_i16(bytes, &p);
            switch (prm_type) {
            case PRM_TYPE_F3:
                prm.ptr = mem_bump(sizeof(F3));
                prm.f3->coords[0] = get_i16(bytes, &p);
                prm.f3->coords[1] = get_i16(bytes, &p);
                prm.f3->coords[2] = get_i16(bytes, &p);
                prm.f3->pad1 = get_i16(bytes, &p);
                prm.f3->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_F4:
                prm.ptr = mem_bump(sizeof(F4));
                prm.f4->coords[0] = get_i16(bytes, &p);
                prm.f4->coords[1] = get_i16(bytes, &p);
                prm.f4->coords[2] = get_i16(bytes, &p);
                prm.f4->coords[3] = get_i16(bytes, &p);
                prm.f4->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_FT3:
                prm.ptr = mem_bump(sizeof(FT3));
                prm.ft3->coords[0] = get_i16(bytes, &p);
                prm.ft3->coords[1] = get_i16(bytes, &p);
                prm.ft3->coords[2] = get_i16(bytes, &p);
                prm.ft3->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.ft3->cba = get_i16(bytes, &p);
                prm.ft3->tsb = get_i16(bytes, &p);
                prm.ft3->u0 = get_i8(bytes, &p);
                prm.ft3->v0 = get_i8(bytes, &p);
                prm.ft3->u1 = get_i8(bytes, &p);
                prm.ft3->v1 = get_i8(bytes, &p);
                prm.ft3->u2 = get_i8(bytes, &p);
                prm.ft3->v2 = get_i8(bytes, &p);
                prm.ft3->pad1 = get_i16(bytes, &p);
                prm.ft3->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_FT4:
                prm.ptr = mem_bump(sizeof(FT4));
                prm.ft4->coords[0] = get_i16(bytes, &p);
                prm.ft4->coords[1] = get_i16(bytes, &p);
                prm.ft4->coords[2] = get_i16(bytes, &p);
                prm.ft4->coords[3] = get_i16(bytes, &p);
                prm.ft4->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.ft4->cba = get_i16(bytes, &p);
                prm.ft4->tsb = get_i16(bytes, &p);
                prm.ft4->u0 = get_i8(bytes, &p);
                prm.ft4->v0 = get_i8(bytes, &p);
                prm.ft4->u1 = get_i8(bytes, &p);
                prm.ft4->v1 = get_i8(bytes, &p);
                prm.ft4->u2 = get_i8(bytes, &p);
                prm.ft4->v2 = get_i8(bytes, &p);
                prm.ft4->u3 = get_i8(bytes, &p);
                prm.ft4->v3 = get_i8(bytes, &p);
                prm.ft4->pad1 = get_i16(bytes, &p);
                prm.ft4->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_G3:
                prm.ptr = mem_bump(sizeof(G3));
                prm.g3->coords[0] = get_i16(bytes, &p);
                prm.g3->coords[1] = get_i16(bytes, &p);
                prm.g3->coords[2] = get_i16(bytes, &p);
                prm.g3->pad1 = get_i16(bytes, &p);
                prm.g3->color[0] = rgba_from_u32(get_u32(bytes, &p));
                prm.g3->color[1] = rgba_from_u32(get_u32(bytes, &p));
                prm.g3->color[2] = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_G4:
                prm.ptr = mem_bump(sizeof(G4));
                prm.g4->coords[0] = get_i16(bytes, &p);
                prm.g4->coords[1] = get_i16(bytes, &p);
                prm.g4->coords[2] = get_i16(bytes, &p);
                prm.g4->coords[3] = get_i16(bytes, &p);
                prm.g4->color[0] = rgba_from_u32(get_u32(bytes, &p));
                prm.g4->color[1] = rgba_from_u32(get_u32(bytes, &p));
                prm.g4->color[2] = rgba_from_u32(get_u32(bytes, &p));
                prm.g4->color[3] = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_GT3:
                prm.ptr = mem_bump(sizeof(GT3));
                prm.gt3->coords[0] = get_i16(bytes, &p);
                prm.gt3->coords[1] = get_i16(bytes, &p);
                prm.gt3->coords[2] = get_i16(bytes, &p);
                prm.gt3->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.gt3->cba = get_i16(bytes, &p);
                prm.gt3->tsb = get_i16(bytes, &p);
                prm.gt3->u0 = get_i8(bytes, &p);
                prm.gt3->v0 = get_i8(bytes, &p);
                prm.gt3->u1 = get_i8(bytes, &p);
                prm.gt3->v1 = get_i8(bytes, &p);
                prm.gt3->u2 = get_i8(bytes, &p);
                prm.gt3->v2 = get_i8(bytes, &p);
                prm.gt3->pad1 = get_i16(bytes, &p);
                prm.gt3->color[0] = rgba_from_u32(get_u32(bytes, &p));
                prm.gt3->color[1] = rgba_from_u32(get_u32(bytes, &p));
                prm.gt3->color[2] = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_GT4:
                prm.ptr = mem_bump(sizeof(GT4));
                prm.gt4->coords[0] = get_i16(bytes, &p);
                prm.gt4->coords[1] = get_i16(bytes, &p);
                prm.gt4->coords[2] = get_i16(bytes, &p);
                prm.gt4->coords[3] = get_i16(bytes, &p);
                prm.gt4->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.gt4->cba = get_i16(bytes, &p);
                prm.gt4->tsb = get_i16(bytes, &p);
                prm.gt4->u0 = get_i8(bytes, &p);
                prm.gt4->v0 = get_i8(bytes, &p);
                prm.gt4->u1 = get_i8(bytes, &p);
                prm.gt4->v1 = get_i8(bytes, &p);
                prm.gt4->u2 = get_i8(bytes, &p);
                prm.gt4->v2 = get_i8(bytes, &p);
                prm.gt4->u3 = get_i8(bytes, &p);
                prm.gt4->v3 = get_i8(bytes, &p);
                prm.gt4->pad1 = get_i16(bytes, &p);
                prm.gt4->color[0] = rgba_from_u32(get_u32(bytes, &p));
                prm.gt4->color[1] = rgba_from_u32(get_u32(bytes, &p));
                prm.gt4->color[2] = rgba_from_u32(get_u32(bytes, &p));
                prm.gt4->color[3] = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_LSF3:
                prm.ptr = mem_bump(sizeof(LSF3));
                prm.lsf3->coords[0] = get_i16(bytes, &p);
                prm.lsf3->coords[1] = get_i16(bytes, &p);
                prm.lsf3->coords[2] = get_i16(bytes, &p);
                prm.lsf3->normal = get_i16(bytes, &p);
                prm.lsf3->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_LSF4:
                prm.ptr = mem_bump(sizeof(LSF4));
                prm.lsf4->coords[0] = get_i16(bytes, &p);
                prm.lsf4->coords[1] = get_i16(bytes, &p);
                prm.lsf4->coords[2] = get_i16(bytes, &p);
                prm.lsf4->coords[3] = get_i16(bytes, &p);
                prm.lsf4->normal = get_i16(bytes, &p);
                prm.lsf4->pad1 = get_i16(bytes, &p);
                prm.lsf4->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_LSFT3:
                prm.ptr = mem_bump(sizeof(LSFT3));
                prm.lsft3->coords[0] = get_i16(bytes, &p);
                prm.lsft3->coords[1] = get_i16(bytes, &p);
                prm.lsft3->coords[2] = get_i16(bytes, &p);
                prm.lsft3->normal = get_i16(bytes, &p);
                prm.lsft3->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.lsft3->cba = get_i16(bytes, &p);
                prm.lsft3->tsb = get_i16(bytes, &p);
                prm.lsft3->u0 = get_i8(bytes, &p);
                prm.lsft3->v0 = get_i8(bytes, &p);
                prm.lsft3->u1 = get_i8(bytes, &p);
                prm.lsft3->v1 = get_i8(bytes, &p);
                prm.lsft3->u2 = get_i8(bytes, &p);
                prm.lsft3->v2 = get_i8(bytes, &p);
                prm.lsft3->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_LSFT4:
                prm.ptr = mem_bump(sizeof(LSFT4));
                prm.lsft4->coords[0] = get_i16(bytes, &p);
                prm.lsft4->coords[1] = get_i16(bytes, &p);
                prm.lsft4->coords[2] = get_i16(bytes, &p);
                prm.lsft4->coords[3] = get_i16(bytes, &p);
                prm.lsft4->normal = get_i16(bytes, &p);
                prm.lsft4->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.lsft4->cba = get_i16(bytes, &p);
                prm.lsft4->tsb = get_i16(bytes, &p);
                prm.lsft4->u0 = get_i8(bytes, &p);
                prm.lsft4->v0 = get_i8(bytes, &p);
                prm.lsft4->u1 = get_i8(bytes, &p);
                prm.lsft4->v1 = get_i8(bytes, &p);
                prm.lsft4->u2 = get_i8(bytes, &p);
                prm.lsft4->v2 = get_i8(bytes, &p);
                prm.lsft4->u3 = get_i8(bytes, &p);
                prm.lsft4->v3 = get_i8(bytes, &p);
                prm.lsft4->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_LSG3:
                prm.ptr = mem_bump(sizeof(LSG3));
                prm.lsg3->coords[0] = get_i16(bytes, &p);
                prm.lsg3->coords[1] = get_i16(bytes, &p);
                prm.lsg3->coords[2] = get_i16(bytes, &p);
                prm.lsg3->normals[0] = get_i16(bytes, &p);
                prm.lsg3->normals[1] = get_i16(bytes, &p);
                prm.lsg3->normals[2] = get_i16(bytes, &p);
                prm.lsg3->color[0] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsg3->color[1] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsg3->color[2] = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_LSG4:
                prm.ptr = mem_bump(sizeof(LSG4));
                prm.lsg4->coords[0] = get_i16(bytes, &p);
                prm.lsg4->coords[1] = get_i16(bytes, &p);
                prm.lsg4->coords[2] = get_i16(bytes, &p);
                prm.lsg4->coords[3] = get_i16(bytes, &p);
                prm.lsg4->normals[0] = get_i16(bytes, &p);
                prm.lsg4->normals[1] = get_i16(bytes, &p);
                prm.lsg4->normals[2] = get_i16(bytes, &p);
                prm.lsg4->normals[3] = get_i16(bytes, &p);
                prm.lsg4->color[0] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsg4->color[1] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsg4->color[2] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsg4->color[3] = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_LSGT3:
                prm.ptr = mem_bump(sizeof(LSGT3));
                prm.lsgt3->coords[0] = get_i16(bytes, &p);
                prm.lsgt3->coords[1] = get_i16(bytes, &p);
                prm.lsgt3->coords[2] = get_i16(bytes, &p);
                prm.lsgt3->normals[0] = get_i16(bytes, &p);
                prm.lsgt3->normals[1] = get_i16(bytes, &p);
                prm.lsgt3->normals[2] = get_i16(bytes, &p);
                prm.lsgt3->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.lsgt3->cba = get_i16(bytes, &p);
                prm.lsgt3->tsb = get_i16(bytes, &p);
                prm.lsgt3->u0 = get_i8(bytes, &p);
                prm.lsgt3->v0 = get_i8(bytes, &p);
                prm.lsgt3->u1 = get_i8(bytes, &p);
                prm.lsgt3->v1 = get_i8(bytes, &p);
                prm.lsgt3->u2 = get_i8(bytes, &p);
                prm.lsgt3->v2 = get_i8(bytes, &p);
                prm.lsgt3->color[0] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsgt3->color[1] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsgt3->color[2] = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_LSGT4:
                prm.ptr = mem_bump(sizeof(LSGT4));
                prm.lsgt4->coords[0] = get_i16(bytes, &p);
                prm.lsgt4->coords[1] = get_i16(bytes, &p);
                prm.lsgt4->coords[2] = get_i16(bytes, &p);
                prm.lsgt4->coords[3] = get_i16(bytes, &p);
                prm.lsgt4->normals[0] = get_i16(bytes, &p);
                prm.lsgt4->normals[1] = get_i16(bytes, &p);
                prm.lsgt4->normals[2] = get_i16(bytes, &p);
                prm.lsgt4->normals[3] = get_i16(bytes, &p);
                prm.lsgt4->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.lsgt4->cba = get_i16(bytes, &p);
                prm.lsgt4->tsb = get_i16(bytes, &p);
                prm.lsgt4->u0 = get_i8(bytes, &p);
                prm.lsgt4->v0 = get_i8(bytes, &p);
                prm.lsgt4->u1 = get_i8(bytes, &p);
                prm.lsgt4->v1 = get_i8(bytes, &p);
                prm.lsgt4->u2 = get_i8(bytes, &p);
                prm.lsgt4->v2 = get_i8(bytes, &p);
                prm.lsgt4->pad1 = get_i16(bytes, &p);
                prm.lsgt4->color[0] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsgt4->color[1] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsgt4->color[2] = rgba_from_u32(get_u32(bytes, &p));
                prm.lsgt4->color[3] = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_TSPR:
            case PRM_TYPE_BSPR:
                prm.ptr = mem_bump(sizeof(SPR));
                prm.spr->coord = get_i16(bytes, &p);
                prm.spr->width = get_i16(bytes, &p);
                prm.spr->height = get_i16(bytes, &p);
                prm.spr->texture = texture_from_list(tl, get_i16(bytes, &p));
                prm.spr->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_SPLINE:
                prm.ptr = mem_bump(sizeof(Spline));
                prm.spline->control1.x = get_i32(bytes, &p);
                prm.spline->control1.y = get_i32(bytes, &p);
                prm.spline->control1.z = get_i32(bytes, &p);
                p += 4; // padding
                prm.spline->position.x = get_i32(bytes, &p);
                prm.spline->position.y = get_i32(bytes, &p);
                prm.spline->position.z = get_i32(bytes, &p);
                p += 4; // padding
                prm.spline->control2.x = get_i32(bytes, &p);
                prm.spline->control2.y = get_i32(bytes, &p);
                prm.spline->control2.z = get_i32(bytes, &p);
                p += 4; // padding
                prm.spline->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            case PRM_TYPE_POINT_LIGHT:
                prm.ptr = mem_bump(sizeof(PointLight));
                prm.pointLight->position.x = get_i32(bytes, &p);
                prm.pointLight->position.y = get_i32(bytes, &p);
                prm.pointLight->position.z = get_i32(bytes, &p);
                p += 4; // padding
                prm.pointLight->color = rgba_from_u32(get_u32(bytes, &p));
                prm.pointLight->startFalloff = get_i16(bytes, &p);
                prm.pointLight->endFalloff = get_i16(bytes, &p);
                break;
            case PRM_TYPE_SPOT_LIGHT:
                prm.ptr = mem_bump(sizeof(SpotLight));
                prm.spotLight->position.x = get_i32(bytes, &p);
                prm.spotLight->position.y = get_i32(bytes, &p);
                prm.spotLight->position.z = get_i32(bytes, &p);
                p += 4; // padding
                prm.spotLight->direction.x = get_i16(bytes, &p);
                prm.spotLight->direction.y = get_i16(bytes, &p);
                prm.spotLight->direction.z = get_i16(bytes, &p);
                p += 2; // padding
                prm.spotLight->color = rgba_from_u32(get_u32(bytes, &p));
                prm.spotLight->startFalloff = get_i16(bytes, &p);
                prm.spotLight->endFalloff = get_i16(bytes, &p);
                prm.spotLight->coneAngle = get_i16(bytes, &p);
                prm.spotLight->spreadAngle = get_i16(bytes, &p);
                break;
            case PRM_TYPE_INFINITE_LIGHT:
                prm.ptr = mem_bump(sizeof(InfiniteLight));
                prm.infiniteLight->direction.x = get_i16(bytes, &p);
                prm.infiniteLight->direction.y = get_i16(bytes, &p);
                prm.infiniteLight->direction.z = get_i16(bytes, &p);
                p += 2; // padding
                prm.infiniteLight->color = rgba_from_u32(get_u32(bytes, &p));
                break;
            default:
                die("bad primitive type %x \n", prm_type);
            } // switch
            prm.f3->type = prm_type;
            prm.f3->flag = prm_flag;
        } // each prim
    } // each object
    mem_temp_free(bytes);
    return objectList;
}