package amf import ( "errors" "encoding/binary" "math" ) type AMFObj map[interface{}]interface{} func DecodeAMF(data *[]byte) (AMFObj, error) { var byte_idx uint32 var root_obj_idx int amf_root_obj := make(AMFObj) for { top_level_obj, err := read_next(data, &byte_idx) if err != nil { return amf_root_obj, err } amf_root_obj[root_obj_idx] = top_level_obj root_obj_idx += 1 if byte_idx == uint32(len(*data)) { break } } return amf_root_obj, nil } func read_bytes(data *[]byte, byte_idx *uint32, n uint32) ([]byte, error) { if int(*byte_idx + n) > len(*data) { return make([]byte, 0), errors.New("Read goes past end") } read_slice := (*data)[*byte_idx:*byte_idx + n] *byte_idx += n return read_slice, nil } func read_number(data *[]byte, byte_idx *uint32) (float64, error) { float_bytes, err := read_bytes(data, byte_idx, 8) if err != nil { return 0.0, err } return math.Float64frombits(binary.BigEndian.Uint64(float_bytes)), nil } func read_bool(data *[]byte, byte_idx *uint32) (bool, error) { bool_byte, err := read_bytes(data, byte_idx, 1) if err != nil { return false, err } if bool_byte[0] > 1 { return false, errors.New("bool byte must be 0 or 1") } return bool_byte[0] != 0, nil } func read_string(data *[]byte, byte_idx *uint32) (string, error) { string_len, err := read_bytes(data, byte_idx, 2) if err != nil { return "", err } string_bytes, err := read_bytes(data, byte_idx, uint32(binary.BigEndian.Uint16(string_len))) if err != nil { return "", err } return string(string_bytes), err } func read_object(data *[]byte, byte_idx *uint32) (AMFObj, error) { root_obj := make(AMFObj) for { key, err := read_string(data, byte_idx) if err != nil { return root_obj, err } if key == "" { if end_byte, err := read_bytes(data, byte_idx, 1); err != nil { return root_obj, err } else if end_byte[0] != 9 { return root_obj, errors.New("Object should end but didnt") } return root_obj, nil } val, err := read_next(data, byte_idx) if err != nil { return root_obj, err } root_obj[key] = val } } func read_next(data *[]byte, byte_idx *uint32) (interface{}, error) { data_type, err := read_bytes(data, byte_idx, 1) var next_obj interface{} if err != nil { return nil, err } switch data_type[0] { case 0: next_obj, err = read_number(data, byte_idx) case 1: next_obj, err = read_bool(data, byte_idx) case 2: next_obj, err = read_string(data, byte_idx) case 3: next_obj, err = read_object(data, byte_idx) case 5: return nil, nil default: return nil, errors.New("Unhandled data type") } if err != nil { return nil, err } return next_obj, nil }