package nex import ( "errors" "reflect" "strings" crunch "github.com/superwhiskers/crunch/v3" ) // StreamIn is an input stream abstraction of github.com/superwhiskers/crunch with nex type support type StreamIn struct { *crunch.Buffer Server *Server } // ReadBool reads a bool func (stream *StreamIn) ReadBool() bool { return (stream.ReadByteNext() == 1) } // ReadUInt8 reads a uint8 func (stream *StreamIn) ReadUInt8() uint8 { return uint8(stream.ReadByteNext()) } // ReadUInt16LE reads a uint16 func (stream *StreamIn) ReadUInt16LE() uint16 { return stream.ReadU16LENext(1)[0] } // ReadUInt32LE reads a uint32 func (stream *StreamIn) ReadUInt32LE() uint32 { return stream.ReadU32LENext(1)[0] } // ReadUInt32BE reads a uint32 func (stream *StreamIn) ReadUInt32BE() uint32 { return stream.ReadU32BENext(1)[0] } // ReadInt32LE reads a int32 func (stream *StreamIn) ReadInt32LE() int32 { return int32(stream.ReadU32LENext(1)[0]) } // ReadUInt64LE reads a uint64 func (stream *StreamIn) ReadUInt64LE() uint64 { return stream.ReadU64LENext(1)[0] } // ReadUInt64BE reads a uint64 func (stream *StreamIn) ReadUInt64BE() uint64 { return stream.ReadU64BENext(1)[0] } // ReadString reads and returns a nex string type func (stream *StreamIn) ReadString() (string, error) { length := stream.ReadUInt16LE() if len(stream.Bytes()[stream.ByteOffset():]) < int(length) { return "", errors.New("[StreamIn] Nex string length longer than data size") } stringData := stream.ReadBytesNext(int64(length)) str := string(stringData) return strings.TrimRight(str, "\x00"), nil } // ReadBuffer reads a nex Buffer type func (stream *StreamIn) ReadBuffer() ([]byte, error) { length := stream.ReadUInt32LE() if len(stream.Bytes()[stream.ByteOffset():]) < int(length) { return []byte{}, errors.New("[StreamIn] Nex buffer length longer than data size") } data := stream.ReadBytesNext(int64(length)) return data, nil } // ReadQBuffer reads a nex qBuffer type func (stream *StreamIn) ReadQBuffer() ([]byte, error) { length := stream.ReadUInt16LE() if len(stream.Bytes()[stream.ByteOffset():]) < int(length) { return []byte{}, errors.New("[StreamIn] Nex qBuffer length longer than data size") } data := stream.ReadBytesNext(int64(length)) return data, nil } // ReadStructure reads a nex Structure type func (stream *StreamIn) ReadStructure(structure StructureInterface) (StructureInterface, error) { hierarchy := structure.Hierarchy() for _, class := range hierarchy { _, err := stream.ReadStructure(class) if err != nil { return structure, errors.New("[ReadStructure] " + err.Error()) } } nexVersion := stream.Server.NEXVersion() if nexVersion.Major >= 3 && nexVersion.Minor >= 5 { // skip the new struct header as we don't really need the data there _ = stream.ReadUInt8() // structure header version _ = stream.ReadUInt32LE() // structure content length } err := structure.ExtractFromStream(stream) if err != nil { return structure, errors.New("[ReadStructure] " + err.Error()) } return structure, nil } // ReadVariant reads a Variant type. This type can hold 7 different types func (stream *StreamIn) ReadVariant() interface{} { switch stream.ReadUInt8() { case 0: // null return nil case 1: // sint64 return int64(stream.ReadUInt64LE()) case 2: // double return float64(stream.ReadUInt64LE()) case 3: // bool return stream.ReadUInt8() == 1 case 4: // string str, _ := stream.ReadString() return str case 5: // datetime return NewDateTime(stream.ReadUInt64LE()) case 6: // uint64 return stream.ReadUInt64LE() } return nil } // ReadMap reads a Map type with the given key and value types func (stream *StreamIn) ReadMap(keyFunction interface{}, valueFunction interface{}) (map[interface{}]interface{}, error) { /* TODO: Make this not suck Map types can have any type as the key and any type as the value Due to strict typing we cannot just pass stream functions as these values and call them At the moment this just reads what type you want from the interface{} function type */ length := stream.ReadUInt32LE() newMap := make(map[interface{}]interface{}) for i := 0; i < int(length); i++ { var key interface{} var value interface{} var err error switch keyFunction.(type) { case func() (string, error): key, err = stream.ReadString() } if err != nil { return nil, err } switch valueFunction.(type) { case func() interface{}: value = stream.ReadVariant() } newMap[key] = value } return newMap, nil } // ReadDateTime reads a DateTime type func (stream *StreamIn) ReadDateTime() *DateTime { return NewDateTime(stream.ReadUInt64LE()) } // ReadDataHolder reads a DataHolder type func (stream *StreamIn) ReadDataHolder() *DataHolder { dataHolder := NewDataHolder() err := dataHolder.ExtractFromStream(stream) if err != nil { // TODO - Make this maybe return an error? logger.Error(err.Error()) } return dataHolder } // ReadStationURL reads a StationURL type func (stream *StreamIn) ReadStationURL() *StationURL { stationString, err := stream.ReadString() if err != nil { // TODO - Make this maybe return an error? logger.Error(err.Error()) } return NewStationURL(stationString) } // ReadListUInt8 reads a list of uint8 types func (stream *StreamIn) ReadListUInt8() []uint8 { length := stream.ReadUInt32LE() list := make([]uint8, 0, length) for i := 0; i < int(length); i++ { value := stream.ReadUInt8() list = append(list, value) } return list } // ReadListUInt16LE reads a list of uint16 types func (stream *StreamIn) ReadListUInt16LE() []uint16 { length := stream.ReadUInt32LE() list := make([]uint16, 0, length) for i := 0; i < int(length); i++ { value := stream.ReadUInt16LE() list = append(list, value) } return list } // ReadListUInt32LE reads a list of uint32 types func (stream *StreamIn) ReadListUInt32LE() []uint32 { length := stream.ReadUInt32LE() list := make([]uint32, 0, length) for i := 0; i < int(length); i++ { value := stream.ReadUInt32LE() list = append(list, value) } return list } // ReadListInt32LE reads a list of int32 types func (stream *StreamIn) ReadListInt32LE() []int32 { length := stream.ReadUInt32LE() list := make([]int32, 0, length) for i := 0; i < int(length); i++ { value := stream.ReadInt32LE() list = append(list, value) } return list } // ReadListUInt64LE reads a list of uint64 types func (stream *StreamIn) ReadListUInt64LE() []uint64 { length := stream.ReadUInt32LE() list := make([]uint64, 0, length) for i := 0; i < int(length); i++ { value := stream.ReadUInt64LE() list = append(list, value) } return list } // ReadListString reads a list of NEX String types func (stream *StreamIn) ReadListString() []string { length := stream.ReadUInt32LE() list := make([]string, 0, length) for i := 0; i < int(length); i++ { value, _ := stream.ReadString() list = append(list, value) } return list } // ReadListQBuffer reads a list of NEX QBuffer types func (stream *StreamIn) ReadListQBuffer() [][]byte { length := stream.ReadUInt32LE() list := make([][]byte, 0, length) for i := 0; i < int(length); i++ { value, _ := stream.ReadQBuffer() list = append(list, value) } return list } // ReadListStationURL reads a list of NEX Station URL types func (stream *StreamIn) ReadListStationURL() []*StationURL { length := stream.ReadUInt32LE() list := make([]*StationURL, 0, length) for i := 0; i < int(length); i++ { value := stream.ReadStationURL() list = append(list, value) } return list } // ReadListStructure reads and returns a list structure types func (stream *StreamIn) ReadListStructure(structure StructureInterface) (interface{}, error) { length := stream.ReadUInt32LE() structureType := reflect.TypeOf(structure) structureSlice := reflect.MakeSlice(reflect.SliceOf(structureType), 0, int(length)) for i := 0; i < int(length); i++ { newStructure := reflect.New(reflect.TypeOf(structure).Elem()).Interface().(StructureInterface) extractedStructure, err := stream.ReadStructure(newStructure) if err != nil { return nil, err } structureSlice = reflect.Append(structureSlice, reflect.ValueOf(extractedStructure)) } return structureSlice.Interface(), nil } // NewStreamIn returns a new NEX input stream func NewStreamIn(data []byte, server *Server) *StreamIn { return &StreamIn{ Buffer: crunch.NewBuffer(data), Server: server, } }