package gateway import "encoding/binary" type FeedbackKind uint8 const ( FeedbackIDR FeedbackKind = iota + 1 FeedbackFEC ) const ( gatewayFeedbackHeaderSize = 8 gatewayFeedbackClient = 0 gatewayFeedbackGateway = 1 gatewayFeedbackIDR = 1 gatewayFeedbackFEC = 2 gatewayFeedbackTerminated = 0x10 gatewayFeedbackRumble = 0x11 gatewayFeedbackHDR = 0x12 gatewayFeedbackDisconnected = 0x13 ) type gatewayFeedbackMessage struct { direction byte kind byte payload []byte } func EncodeProviderEvent(event ProviderEvent) ([]byte, error) { switch event.Kind { case ProviderEventTerminated: if len(event.Payload) != 4 { return nil, ErrProviderMalformed } return encodeGatewayFeedback(gatewayFeedbackGateway, gatewayFeedbackTerminated, event.Payload) case ProviderEventRumble: if len(event.Payload) != 5 || event.Payload[0] > 15 { return nil, ErrProviderMalformed } return encodeGatewayFeedback(gatewayFeedbackGateway, gatewayFeedbackRumble, event.Payload) case ProviderEventHDR: if len(event.Payload) != 1 || event.Payload[0] > 1 { return nil, ErrProviderMalformed } return encodeGatewayFeedback(gatewayFeedbackGateway, gatewayFeedbackHDR, event.Payload) case ProviderEventDisconnected: if len(event.Payload) != 0 { return nil, ErrProviderMalformed } return encodeGatewayFeedback(gatewayFeedbackGateway, gatewayFeedbackDisconnected, nil) default: return nil, ErrProviderMalformed } } func EncodeClientFeedback(feedback Feedback) ([]byte, error) { var kind byte switch feedback.Kind { case FeedbackIDR: kind = gatewayFeedbackIDR if len(feedback.Payload) != 0 { return nil, ErrProviderMalformed } case FeedbackFEC: kind = gatewayFeedbackFEC if !validGatewayFECStatus(feedback.Payload) { return nil, ErrProviderMalformed } default: return nil, ErrProviderMalformed } return encodeGatewayFeedback(gatewayFeedbackClient, kind, feedback.Payload) } func DecodeClientFeedback(data []byte) (Feedback, error) { message, err := decodeGatewayFeedback(data) if err != nil || message.direction != gatewayFeedbackClient { return Feedback{}, ErrProviderMalformed } switch message.kind { case gatewayFeedbackIDR: if len(message.payload) != 0 { return Feedback{}, ErrProviderMalformed } return Feedback{Kind: FeedbackIDR}, nil case gatewayFeedbackFEC: if !validGatewayFECStatus(message.payload) { return Feedback{}, ErrProviderMalformed } return Feedback{Kind: FeedbackFEC, Payload: message.payload}, nil default: return Feedback{}, ErrProviderMalformed } } func validGatewayFECStatus(payload []byte) bool { if len(payload) != 21 { return false } totalData := binary.BigEndian.Uint16(payload[10:12]) totalParity := binary.BigEndian.Uint16(payload[12:14]) receivedData := binary.BigEndian.Uint16(payload[14:16]) receivedParity := binary.BigEndian.Uint16(payload[16:18]) return totalData > 0 && receivedData <= totalData && receivedParity <= totalParity && payload[18] <= 100 && payload[20] > 0 && payload[19] < payload[20] } func DecodeProviderEvent(data []byte) (ProviderEvent, error) { message, err := decodeGatewayFeedback(data) if err != nil || message.direction != gatewayFeedbackGateway { return ProviderEvent{}, ErrProviderMalformed } switch message.kind { case gatewayFeedbackTerminated: if len(message.payload) != 4 { return ProviderEvent{}, ErrProviderMalformed } return ProviderEvent{Kind: ProviderEventTerminated, Payload: message.payload}, nil case gatewayFeedbackRumble: if len(message.payload) != 5 || message.payload[0] > 15 { return ProviderEvent{}, ErrProviderMalformed } return ProviderEvent{Kind: ProviderEventRumble, Payload: message.payload}, nil case gatewayFeedbackHDR: if len(message.payload) != 1 || message.payload[0] > 1 { return ProviderEvent{}, ErrProviderMalformed } return ProviderEvent{Kind: ProviderEventHDR, Payload: message.payload}, nil case gatewayFeedbackDisconnected: if len(message.payload) != 0 { return ProviderEvent{}, ErrProviderMalformed } return ProviderEvent{Kind: ProviderEventDisconnected}, nil default: return ProviderEvent{}, ErrProviderMalformed } } func encodeGatewayFeedback(direction, kind byte, payload []byte) ([]byte, error) { if len(payload) > 1016 { return nil, ErrProviderMalformed } encoded := make([]byte, gatewayFeedbackHeaderSize+len(payload)) copy(encoded, "VGF1") encoded[4], encoded[5] = direction, kind binary.BigEndian.PutUint16(encoded[6:8], uint16(len(payload))) copy(encoded[8:], payload) return encoded, nil } func decodeGatewayFeedback(data []byte) (gatewayFeedbackMessage, error) { if len(data) < gatewayFeedbackHeaderSize || len(data) > 1024 || string(data[:4]) != "VGF1" || len(data) != gatewayFeedbackHeaderSize+int(binary.BigEndian.Uint16(data[6:8])) { return gatewayFeedbackMessage{}, ErrProviderMalformed } message := gatewayFeedbackMessage{direction: data[4], kind: data[5], payload: append([]byte(nil), data[8:]...)} if message.direction != gatewayFeedbackClient && message.direction != gatewayFeedbackGateway { return gatewayFeedbackMessage{}, ErrProviderMalformed } return message, nil }