Files
Odin/core/encoding/hxa/write.odin
2021-08-09 16:26:51 +01:00

194 lines
4.7 KiB
Odin

package encoding_hxa
import "core:os"
import "core:mem"
Write_Error :: enum {
None,
Buffer_Too_Small,
Failed_File_Write,
}
write_to_file :: proc(filepath: string, file: File) -> (err: Write_Error) {
required := required_write_size(file);
buf, alloc_err := make([]byte, required);
if alloc_err == .Out_Of_Memory {
return .Failed_File_Write;
}
defer delete(buf);
write_internal(&Writer{data = buf}, file);
if !os.write_entire_file(filepath, buf) {
err =.Failed_File_Write;
}
return;
}
write :: proc(buf: []byte, file: File) -> (n: int, err: Write_Error) {
required := required_write_size(file);
if len(buf) < required {
err = .Buffer_Too_Small;
return;
}
n = required;
write_internal(&Writer{data = buf}, file);
return;
}
required_write_size :: proc(file: File) -> (n: int) {
writer := &Writer{dummy_pass = true};
write_internal(writer, file);
n = writer.offset;
return;
}
@(private)
Writer :: struct {
data: []byte,
offset: int,
dummy_pass: bool,
};
@(private)
write_internal :: proc(w: ^Writer, file: File) {
write_value :: proc(w: ^Writer, value: $T) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(T) <= remaining);
ptr := raw_data(w.data[w.offset:]);
(^T)(ptr)^ = value;
}
w.offset += size_of(T);
}
write_array :: proc(w: ^Writer, array: []$T) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(T)*len(array) <= remaining);
ptr := raw_data(w.data[w.offset:]);
dst := mem.slice_ptr((^T)(ptr), len(array));
copy(dst, array);
}
w.offset += size_of(T)*len(array);
}
write_string :: proc(w: ^Writer, str: string) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(byte)*len(str) <= remaining);
ptr := raw_data(w.data[w.offset:]);
dst := mem.slice_ptr((^byte)(ptr), len(str));
copy(dst, str);
}
w.offset += size_of(byte)*len(str);
}
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
for m in meta_data {
name_len := max(len(m.name), 255);
write_value(w, u8(name_len));
write_string(w, m.name[:name_len]);
meta_data_type: Meta_Value_Type;
length: u32le = 0;
switch v in m.value {
case []i64le:
meta_data_type = .Int64;
length = u32le(len(v));
case []f64le:
meta_data_type = .Double;
length = u32le(len(v));
case []Node_Index:
meta_data_type = .Node;
length = u32le(len(v));
case string:
meta_data_type = .Text;
length = u32le(len(v));
case []byte:
meta_data_type = .Binary;
length = u32le(len(v));
case []Meta:
meta_data_type = .Meta;
length = u32le(len(v));
}
write_value(w, meta_data_type);
write_value(w, length);
switch v in m.value {
case []i64le: write_array(w, v);
case []f64le: write_array(w, v);
case []Node_Index: write_array(w, v);
case string: write_string(w, v);
case []byte: write_array(w, v);
case []Meta: write_metadata(w, v);
}
}
return;
}
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
write_value(w, u32(len(layers)));
for layer in layers {
name_len := max(len(layer.name), 255);
write_value(w, u8(name_len));
write_string(w, layer .name[:name_len]);
write_value(w, layer.components);
layer_data_type: Layer_Data_Type;
switch v in layer.data {
case []u8: layer_data_type = .Uint8;
case []i32le: layer_data_type = .Int32;
case []f32le: layer_data_type = .Float;
case []f64le: layer_data_type = .Double;
}
write_value(w, layer_data_type);
switch v in layer.data {
case []u8: write_array(w, v);
case []i32le: write_array(w, v);
case []f32le: write_array(w, v);
case []f64le: write_array(w, v);
}
}
return;
}
write_value(w, &Header{
magic_number = MAGIC_NUMBER,
version = LATEST_VERSION,
internal_node_count = u32le(len(file.nodes)),
});
for node in file.nodes {
node_type: Node_Type;
switch content in node.content {
case Node_Geometry: node_type = .Geometry;
case Node_Image: node_type = .Image;
}
write_value(w, node_type);
write_value(w, u32(len(node.meta_data)));
write_metadata(w, node.meta_data);
switch content in node.content {
case Node_Geometry:
write_value(w, content.vertex_count);
write_layer_stack(w, content.vertex_stack);
write_value(w, content.edge_corner_count);
write_layer_stack(w, content.corner_stack);
write_layer_stack(w, content.edge_stack);
write_value(w, content.face_count);
write_layer_stack(w, content.face_stack);
case Node_Image:
write_value(w, content.type);
dimensions := int(content.type);
if content.type == .Image_Cube {
dimensions = 2;
}
for d in 0..<dimensions {
write_value(w, content.resolution[d]);
}
write_layer_stack(w, content.image_stack);
}
}
}