package encoding_hxa import "core:fmt" import "core:os" import "core:mem" Read_Error :: enum { None, Short_Read, Invalid_Data, Unable_To_Read_File, } read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) { context.allocator = allocator; data, ok := os.read_entire_file(filename); if !ok { err = .Unable_To_Read_File; return; } defer if !ok { delete(data); } else { file.backing = data; } file, err = read(data, filename, print_error, allocator); return; } read :: proc(data: []byte, filename := "", print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) { Reader :: struct { filename: string, data: []byte, offset: int, print_error: bool, }; read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) { remaining := len(r.data) - r.offset; if remaining < size_of(T) { err = .Short_Read; return; } ptr := raw_data(r.data[r.offset:]); value = (^T)(ptr)^; r.offset += size_of(T); return; } read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) { remaining := len(r.data) - r.offset; if remaining < size_of(T)*count { err = .Short_Read; return; } ptr := raw_data(r.data[r.offset:]); value = mem.slice_ptr((^T)(ptr), count); r.offset += size_of(T)*count; return; } read_string :: proc(r: ^Reader, count: int) -> (string, Read_Error) { buf, err := read_array(r, byte, count); return string(buf), err; } read_name :: proc(r: ^Reader) -> (value: string, err: Read_Error) { len := read_value(r, u8) or_return; data := read_array(r, byte, int(len)) or_return; return string(data[:len]), nil; } read_meta :: proc(r: ^Reader, capacity: u32le) -> (meta_data: []Meta, err: Read_Error) { meta_data = make([]Meta, int(capacity)); count := 0; defer meta_data = meta_data[:count]; for m in &meta_data { m.name = read_name(r) or_return; type := read_value(r, Meta_Value_Type) or_return; if type > max(Meta_Value_Type) { if r.print_error { fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type))); } err = .Invalid_Data; return; } array_length := read_value(r, u32le) or_return; switch type { case .Int64: m.value = read_array(r, i64le, int(array_length)) or_return; case .Double: m.value = read_array(r, f64le, int(array_length)) or_return; case .Node: m.value = read_array(r, Node_Index, int(array_length)) or_return; case .Text: m.value = read_string(r, int(array_length)) or_return; case .Binary: m.value = read_array(r, byte, int(array_length)) or_return; case .Meta: m.value = read_meta(r, array_length) or_return; } count += 1; } return; } read_layer_stack :: proc(r: ^Reader, capacity: u32le) -> (layers: Layer_Stack, err: Read_Error) { stack_count := read_value(r, u32le) or_return; layer_count := 0; layers = make(Layer_Stack, stack_count); defer layers = layers[:layer_count]; for layer in &layers { layer.name = read_name(r) or_return; layer.components = read_value(r, u8) or_return; type := read_value(r, Layer_Data_Type) or_return; if type > max(type) { if r.print_error { fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type))); } err = .Invalid_Data; return; } data_len := int(layer.components) * int(capacity); switch type { case .Uint8: layer.data = read_array(r, u8, data_len) or_return; case .Int32: layer.data = read_array(r, i32le, data_len) or_return; case .Float: layer.data = read_array(r, f32le, data_len) or_return; case .Double: layer.data = read_array(r, f64le, data_len) or_return; } layer_count += 1; } return; } if len(data) < size_of(Header) { return; } context.allocator = allocator; header := cast(^Header)raw_data(data); assert(header.magic_number == MAGIC_NUMBER); r := &Reader{ filename = filename, data = data[:], offset = size_of(Header), print_error = print_error, }; node_count := 0; file.nodes = make([]Node, header.internal_node_count); defer if err != nil { nodes_destroy(file.nodes); file.nodes = nil; } defer file.nodes = file.nodes[:node_count]; for node_idx in 0.. max(Node_Type) { if r.print_error { fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type))); } err = .Invalid_Data; return; } node_count += 1; node.meta_data = read_meta(r, read_value(r, u32le) or_return) or_return; switch type { case .Meta_Only: // Okay case .Geometry: g: Node_Geometry; g.vertex_count = read_value(r, u32le) or_return; g.vertex_stack = read_layer_stack(r, g.vertex_count) or_return; g.edge_corner_count = read_value(r, u32le) or_return; g.corner_stack = read_layer_stack(r, g.edge_corner_count) or_return; if header.version > 2 { g.edge_stack = read_layer_stack(r, g.edge_corner_count) or_return; } g.face_count = read_value(r, u32le) or_return; g.face_stack = read_layer_stack(r, g.face_count) or_return; node.content = g; case .Image: img: Node_Image; img.type = read_value(r, Image_Type) or_return; dimensions := int(img.type); if img.type == .Image_Cube { dimensions = 2; } img.resolution = {1, 1, 1}; for d in 0..