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..