package n9m import ( "bytes" "encoding/binary" "encoding/json" "fmt" "github.com/icza/bitio" "log" "time" ) // Read package func (e *Package) ReadPackage() bool { if len(e.Accum) < 12 { return false } r := bitio.NewReader(bytes.NewBuffer(e.Accum)) e.Version = uint8(r.TryReadBits(2)) e.EncryptionFlag = r.TryReadBool() e.CompressFlag = r.TryReadBool() e.CSRCCount = uint8(r.TryReadBits(4)) e.PayloadType = PayloadType(r.TryReadBits(8)) e.SSRC = SpecialPayloadType((r.TryReadBits(8) | (r.TryReadBits(8) << 8))) if e.EncryptionFlag && e.CompressFlag { // TODO: get snippet, that use this code r.TryReadBits(8 * 4) e.payloadLen = r.TryReadBits(8) r.TryReadBits(3 * 8) if uint64(len(e.Accum)) < e.payloadLen+12 { return false } } else { e.payloadLen = r.TryReadBits(32) // WTF: e.CC is useless for i := uint64(0); i < 1; i++ { e.CSRC[i] = r.TryReadBits(32) } } if e.payloadLen > 1e6 { log.Printf("%v\n", e) log.Panicln("CORRUPTED PACKAGE") } numOfBytes := 0 if e.payloadLen != 0 { e.RawPayload = make([]byte, e.payloadLen) numOfBytes = r.TryRead(e.RawPayload) } else { e.RawPayload = []byte{} } if numOfBytes != int(e.payloadLen) { return false } e.Accum = e.Accum[12+e.payloadLen:] switch e.PayloadType { case PayloadTypeData: if err := json.Unmarshal(e.RawPayload, &e.Payload); err != nil { log.Printf("Error parsing JSON payload: %v", err) return false } case PayloadTypeSpecial: switch e.SSRC { case SpecialPayloadTypeGPS: e.GPS.GPSStatus = e.RawPayload[0] e.GPS.Expand = e.RawPayload[1] e.GPS.Real = e.RawPayload[2] e.GPS.Longitude = float64(binary.BigEndian.Uint32(e.RawPayload[4:8])) / 1e6 e.GPS.Latitude = float64(binary.BigEndian.Uint32(e.RawPayload[8:12])) / 1e6 e.GPS.Speed = float64(binary.BigEndian.Uint32(e.RawPayload[12:16])) / 100 e.GPS.Direction = float64(binary.BigEndian.Uint32(e.RawPayload[16:20])) / 100 e.GPS.Altitude = int32(binary.BigEndian.Uint32(e.RawPayload[20:24])) var err error if e.GPS.Time, err = time.Parse("20060102150405", string(e.RawPayload[24:38])); err != nil { log.Printf("Error parsing time: %v", err) } default: fmt.Println("N9M parser warning: unknown special payload type", e.SSRC) } default: fmt.Println("N9M parser warning: unknown payload type", e.PayloadType) } if r.TryError != nil { log.Printf("TryError encountered: %v", r.TryError) return false } return true } func (e *Package) PackPayload() (err error) { e.RawPayload, err = json.Marshal(e.Payload) e.payloadLen = uint64(len(e.RawPayload)) if e.payloadLen != 0 { e.RawPayload = append(e.RawPayload, 0) e.payloadLen++ } return } func (e *Package) PackPackage() []byte { var err error if err = e.PackPayload(); err != nil { log.Printf("Error while packing payload: %v", err) return []byte{} } return e.PackRawPackage() } func (e *Package) PackRawPackage() []byte { var err error b := &bytes.Buffer{} w := bitio.NewWriter(b) w.TryWriteBits(uint64(e.Version), 2) w.TryWriteBool(e.EncryptionFlag) w.TryWriteBool(e.CompressFlag) w.TryWriteBits(uint64(e.CSRCCount), 4) w.TryWriteBits(uint64(e.PayloadType), 8) w.TryWriteBits(uint64(e.SSRC), 16) w.TryWriteBits(e.payloadLen, 32) // WTF: e.CC is useless for i := uint64(0); i < 1; i++ { w.TryWriteBits(e.CSRC[i], 32) } if e.payloadLen != 0 { w.TryWrite(e.RawPayload) } if err = w.Close(); err != nil { log.Printf("Error while closing writer: %v", err) return []byte{} } return b.Bytes() } func (e *Package) GetParametersAs(parameters any) error { marshal, err := json.Marshal(e.Payload.Parameter) if err != nil { return err } return json.Unmarshal(marshal, parameters) } func (e *Package) SetParameters(parameters any) { e.Payload.Response = struct{}{} e.Payload.Parameter = parameters } func (e *Package) GetResponseAs(response any) error { marshal, err := json.Marshal(e.Payload.Response) if err != nil { return err } return json.Unmarshal(marshal, response) } func (e *Package) SetResponse(response any) { e.Payload.Parameter = struct{}{} e.Payload.Response = response }