36 Commits

Author SHA1 Message Date
Joel Wetzell e0d4b58606 mess around with fuzzing 2026-05-20 17:44:05 -05:00
Joel Wetzell 2df4cd56f7 add size check to chunk datalen 2026-05-20 17:44:01 -05:00
Joel Wetzell 145c13a976 some linting things 2026-05-17 09:25:27 -05:00
Joel Wetzell e7bdeb46ff add basic chunk benchmark 2026-05-14 21:17:51 -05:00
Joel Wetzell a79bca637c chore: go fix 2026-05-09 14:21:06 -05:00
Joel Wetzell eb8c6aba19 use t.Run for tests 2026-02-27 22:30:52 -06:00
Joel Wetzell 0b20f3ffb9 update examples 2025-12-04 17:46:58 -06:00
Joel Wetzell 98a458ca43 switch to using pointers to trackers 2025-12-04 17:46:51 -06:00
Joel Wetzell e3a7268487 fix chunk decoding edge case 2025-12-01 17:21:07 -06:00
jwetzell 77c0be5b35 Merge pull request #4 from jwetzell/chore/organize-types
move types out of decoders package
2025-10-17 15:35:11 -05:00
jwetzell 7b1dc4bfd6 move types out of decoders package 2025-10-17 15:27:33 -05:00
jwetzell 7dd6d45a08 add tests for encoding data tracker fields 2025-10-17 12:42:17 -05:00
jwetzell 631537b473 change property name 2025-10-17 12:41:59 -05:00
jwetzell bdaf7fd327 add test for encoding packet header chunk 2025-10-17 12:39:45 -05:00
jwetzell d53fe09bfc add tests for encoding info related chunks 2025-10-17 12:35:31 -05:00
jwetzell a0ba854fee add tests for decoding packet header chunk 2025-10-17 12:34:27 -05:00
jwetzell 4c508c37f3 add tests for decoding info related chunks 2025-10-17 12:34:13 -05:00
jwetzell c7f503ee99 add tests for decoding tracker and tracker list chunks 2025-10-17 12:33:46 -05:00
jwetzell 75ba023226 add tests for decoding status, timestamp and xyz chunks 2025-10-16 21:41:28 -05:00
jwetzell b1e1367662 add tests for decoding data and info packet chunks 2025-10-16 21:19:45 -05:00
jwetzell 3271c0f82c add GitHub Actions workflow for encoder tests 2025-10-16 13:27:41 -05:00
jwetzell 15cf2e8052 Modify Go version setup and remove coverage report
Updated the Go version setup and removed the code coverage step.
2025-10-16 13:26:12 -05:00
jwetzell f7ceefc203 Merge pull request #3 from jwetzell/chore/chunk-decoder-testing
add test case to check error scenario in chunk decoder
2025-10-16 13:23:34 -05:00
jwetzell 3af4413e84 Update test_decoders.yaml to remove path filter
Removed path filter for the push event in the test_decoders workflow.
2025-10-16 13:19:57 -05:00
jwetzell 017f8ea20f Update Go version from 1.23.1 to 1.25.3 2025-10-16 13:19:05 -05:00
jwetzell 65947914b3 add test case to check error scenario 2025-10-16 13:15:44 -05:00
jwetzell 1bf0c7c0f0 Update coverage profile file name in test workflow 2025-10-16 13:13:23 -05:00
jwetzell 8c2a580b75 add github action to test decoders 2025-10-16 13:08:38 -05:00
jwetzell f8fde1a796 fieldalignmenttool 2024-12-26 14:55:33 -06:00
jwetzell 610344e706 log formatting 2024-12-25 09:34:19 -06:00
jwetzell 0e03f68a59 cleanup logging 2024-12-25 09:30:51 -06:00
jwetzell 0877357b78 have decoder return err 2024-12-24 00:01:51 -06:00
jwetzell 6bc6fb3668 add most basic error handling on the decode side 2024-12-23 23:59:11 -06:00
jwetzell 0c297d606d add basic chunk encoding test 2024-12-21 14:35:54 -06:00
jwetzell f5b944579d consistent use of hex value 2024-12-19 21:50:33 -06:00
jwetzell ea184a936d gofumpt 2024-12-19 21:48:46 -06:00
66 changed files with 2323 additions and 375 deletions
+31
View File
@@ -0,0 +1,31 @@
name: "Test Decoders"
on:
pull_request:
types: [opened, reopened, synchronize]
paths:
- 'internal/decoders/**'
push:
branches:
- 'main'
jobs:
test_decoders:
name: "test decoders"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version-file: "./go.mod"
- name: Test
run: go test -cover -coverprofile=decoders_coverage.txt ./internal/decoders/...
- name: Archive code coverage results
uses: actions/upload-artifact@v4
with:
name: decoders-code-coverage
path: decoders_coverage.txt
+31
View File
@@ -0,0 +1,31 @@
name: "Test Encoders"
on:
pull_request:
types: [opened, reopened, synchronize]
paths:
- 'internal/encoders/**'
push:
branches:
- 'main'
jobs:
test_encoders:
name: "test encoders"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version-file: "./go.mod"
- name: Test
run: go test -cover -coverprofile=encoders_coverage.txt ./internal/encoders/...
- name: Archive code coverage results
uses: actions/upload-artifact@v4
with:
name: encoders-code-coverage
path: encoders_coverage.txt
+5 -3
View File
@@ -1,5 +1,7 @@
package psn
var PACKET_HEADER_SIZE = 16
var MAX_UDP_PACKET_SIZE = 1500
var CHUNK_HEADER_SIZE = 4
const (
PACKET_HEADER_SIZE = 16
MAX_UDP_PACKET_SIZE = 1500
CHUNK_HEADER_SIZE = 4
)
+32 -19
View File
@@ -1,25 +1,26 @@
package psn
import "github.com/jwetzell/psn-go/internal/decoders"
import (
"github.com/jwetzell/psn-go/internal/chunks"
"github.com/jwetzell/psn-go/internal/decoders"
)
type Decoder struct {
lastInfoPacketHeader *decoders.PacketHeaderChunk
lastDataPacketHeader *decoders.PacketHeaderChunk
infoPacketFrames map[uint8][]decoders.InfoPacketChunk
dataPacketFrames map[uint8][]decoders.DataPacketChunk
Trackers map[uint16]*Tracker
SystemName string
infoPacketFrames map[uint8][]chunks.InfoPacketChunk
dataPacketFrames map[uint8][]chunks.DataPacketChunk
Trackers map[uint16]*Tracker
SystemName string
}
func NewDecoder() *Decoder {
var decoder Decoder
decoder.infoPacketFrames = map[uint8][]decoders.InfoPacketChunk{}
decoder.dataPacketFrames = map[uint8][]decoders.DataPacketChunk{}
decoder.infoPacketFrames = map[uint8][]chunks.InfoPacketChunk{}
decoder.dataPacketFrames = map[uint8][]chunks.DataPacketChunk{}
decoder.Trackers = map[uint16]*Tracker{}
return &decoder
}
func (d *Decoder) updateInfo(framePackets []decoders.InfoPacketChunk) {
func (d *Decoder) updateInfo(framePackets []chunks.InfoPacketChunk) {
for _, framePacket := range framePackets {
d.SystemName = framePacket.Data.SystemName.Data.SystemName
for _, infoTrackerChunk := range framePacket.Data.TrackerList.Data.Trackers {
@@ -34,7 +35,7 @@ func (d *Decoder) updateInfo(framePackets []decoders.InfoPacketChunk) {
}
}
func (d *Decoder) updateData(framePackets []decoders.DataPacketChunk) {
func (d *Decoder) updateData(framePackets []chunks.DataPacketChunk) {
for _, framePacket := range framePackets {
for _, dataTrackerChunk := range framePacket.Data.TrackerList.Data.Trackers {
tracker, ok := d.Trackers[dataTrackerChunk.Chunk.Header.Id]
@@ -48,17 +49,25 @@ func (d *Decoder) updateData(framePackets []decoders.DataPacketChunk) {
}
}
func (d *Decoder) Decode(bytes []byte) {
chunk := decoders.DecodeChunk(bytes)
func (d *Decoder) Decode(bytes []byte) error {
chunk, err := decoders.DecodeChunk(bytes)
if chunk.Header.Id == 0x6756 {
infoPacket := decoders.DecodeInfoPacketChunk(bytes)
if err != nil {
return err
}
switch chunk.Header.Id {
case 0x6756:
infoPacket, err := decoders.DecodeInfoPacketChunk(bytes)
if err != nil {
return err
}
currentInfoPacketHeader := infoPacket.Data.PacketHeader
_, ok := d.infoPacketFrames[currentInfoPacketHeader.Data.FrameId]
if !ok {
d.infoPacketFrames[currentInfoPacketHeader.Data.FrameId] = []decoders.InfoPacketChunk{}
d.infoPacketFrames[currentInfoPacketHeader.Data.FrameId] = []chunks.InfoPacketChunk{}
}
d.infoPacketFrames[currentInfoPacketHeader.Data.FrameId] = append(d.infoPacketFrames[currentInfoPacketHeader.Data.FrameId], infoPacket)
@@ -66,14 +75,17 @@ func (d *Decoder) Decode(bytes []byte) {
d.updateInfo(d.infoPacketFrames[currentInfoPacketHeader.Data.FrameId])
delete(d.infoPacketFrames, currentInfoPacketHeader.Data.FrameId)
}
} else if chunk.Header.Id == 0x6755 {
dataPacket := decoders.DecodeDataPacketChunk(bytes)
case 0x6755:
dataPacket, err := decoders.DecodeDataPacketChunk(bytes)
if err != nil {
return err
}
currentInfoPacketHeader := dataPacket.Data.PacketHeader
_, ok := d.dataPacketFrames[currentInfoPacketHeader.Data.FrameId]
if !ok {
d.dataPacketFrames[currentInfoPacketHeader.Data.FrameId] = []decoders.DataPacketChunk{}
d.dataPacketFrames[currentInfoPacketHeader.Data.FrameId] = []chunks.DataPacketChunk{}
}
d.dataPacketFrames[currentInfoPacketHeader.Data.FrameId] = append(d.dataPacketFrames[currentInfoPacketHeader.Data.FrameId], dataPacket)
@@ -82,4 +94,5 @@ func (d *Decoder) Decode(bytes []byte) {
delete(d.dataPacketFrames, currentInfoPacketHeader.Data.FrameId)
}
}
return nil
}
+2 -3
View File
@@ -18,7 +18,7 @@ func (e *Encoder) ResetInfoFrameId() {
e.infoFrameId = 1
}
func (e *Encoder) GetInfoPackets(timestamp uint64, trackers []Tracker) [][]byte {
func (e *Encoder) GetInfoPackets(timestamp uint64, trackers []*Tracker) [][]byte {
systemNameChunk := encoders.EncodeInfoSystemNameChunk(e.SystemName)
trackerChunks := [][]byte{}
@@ -58,8 +58,7 @@ func (e *Encoder) GetInfoPackets(timestamp uint64, trackers []Tracker) [][]byte
return infoPackets
}
func (e *Encoder) GetDataPackets(timestamp uint64, trackers []Tracker) [][]byte {
func (e *Encoder) GetDataPackets(timestamp uint64, trackers []*Tracker) [][]byte {
trackerChunks := [][]byte{}
for _, tracker := range trackers {
+16 -12
View File
@@ -7,11 +7,11 @@ import (
"github.com/jwetzell/psn-go"
)
func getNTrackers(n int) []psn.Tracker {
trackers := []psn.Tracker{}
func getNTrackers(n int) []*psn.Tracker {
trackers := []*psn.Tracker{}
for index := 0; index < n; index++ {
tracker := psn.Tracker{Id: uint16(index), Name: "Tracker"}
for index := range n {
tracker := &psn.Tracker{Id: uint16(index), Name: "Tracker"}
tracker.SetPos(0, 0, 0)
tracker.SetSpeed(0, 0, 0)
tracker.SetOri(0, 0, 0)
@@ -25,7 +25,6 @@ func getNTrackers(n int) []psn.Tracker {
}
func main() {
testSizes := []int{1, 10, 100, 1000}
encoder := psn.Encoder{
SystemName: "test encoder",
@@ -39,7 +38,6 @@ func main() {
benchmark(trackerCount, iterations, encoder, *decoder)
}
}
}
type BenchmarkResults struct {
@@ -63,31 +61,37 @@ func benchmark(trackerCount int, iterations int, encoder psn.Encoder, decoder ps
latestEncodedPackets := [][]byte{}
for index := 0; index < iterations; index++ {
for range iterations {
latestEncodedPackets = encoder.GetDataPackets(uint64(timestamp), trackers)
}
benchmarkResults.data.encode = float64(time.Now().UnixMicro()-dataEncoderStart) / 1000.0
_ = latestEncodedPackets
dataDecodedStart := time.Now().UnixMicro()
for index := 0; index < iterations; index++ {
for range iterations {
for _, packet := range latestEncodedPackets {
decoder.Decode(packet)
err := decoder.Decode(packet)
if err != nil {
fmt.Printf("failed to decode packet, error: %v\n", err)
}
}
}
benchmarkResults.data.decode = float64(time.Now().UnixMicro()-dataDecodedStart) / 1000.0
// INFO
infoEncoderStart := time.Now().UnixMicro()
for index := 0; index < iterations; index++ {
for range iterations {
latestEncodedPackets = encoder.GetInfoPackets(uint64(timestamp), trackers)
}
benchmarkResults.info.encode = float64(time.Now().UnixMicro()-infoEncoderStart) / 1000.0
infoDecodeStart := time.Now().UnixMicro()
for index := 0; index < iterations; index++ {
for range iterations {
for _, packet := range latestEncodedPackets {
decoder.Decode(packet)
err := decoder.Decode(packet)
if err != nil {
fmt.Printf("failed to decode packet, error: %v\n", err)
}
}
}
benchmarkResults.info.decode = float64(time.Now().UnixMicro()-infoDecodeStart) / 1000.0
+19 -8
View File
@@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"fmt"
"log/slog"
"github.com/jwetzell/psn-go"
)
@@ -10,18 +11,28 @@ import (
func main() {
decoder := psn.NewDecoder()
infoPacketBytes := []byte{0x56, 0x67, 0x34, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
infoPacketBytes := []byte{
0x56, 0x67, 0x34, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x0b, 0x00, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x02, 0x00, 0x11,
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31}
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31,
}
dataPacketBytes := []byte{0x55, 0x67, 0x28, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
dataPacketBytes := []byte{
0x55, 0x67, 0x28, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x14, 0x80, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f}
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
}
decoder.Decode(infoPacketBytes)
decoder.Decode(dataPacketBytes)
err := decoder.Decode(infoPacketBytes)
if err != nil {
slog.Error("error decoding", "err", err)
}
err = decoder.Decode(dataPacketBytes)
if err != nil {
slog.Error("error decoding", "err", err)
}
fmt.Printf("decoded: %+v\n", decoder.SystemName)
fmt.Printf("decoded system name: %s\n", decoder.SystemName)
trackers, err := json.MarshalIndent(decoder.Trackers, "", " ")
if err != nil {
@@ -29,5 +40,5 @@ func main() {
return
}
fmt.Println(string(trackers))
fmt.Printf("decoded trackers: \n%s\n", string(trackers))
}
+2 -4
View File
@@ -7,21 +7,20 @@ import (
)
func main() {
encoder := psn.Encoder{
SystemName: "Server Name",
VersionHigh: 2,
VersionLow: 3,
}
tracker := psn.Tracker{
tracker := &psn.Tracker{
Id: 1,
Name: "Tracker 1",
}
tracker.SetPos(1.0, 1.0, 1.0)
trackers := []psn.Tracker{
trackers := []*psn.Tracker{
tracker,
}
@@ -41,5 +40,4 @@ func main() {
fmt.Printf("%v\n", infoPacket)
}
}
+8 -6
View File
@@ -2,6 +2,7 @@ package main
import (
"fmt"
"log/slog"
"net"
"time"
@@ -11,14 +12,13 @@ import (
func main() {
addr, err := net.ResolveUDPAddr("udp", "236.10.10.10:56565")
if err != nil {
fmt.Printf("Error %v\n", err)
slog.Error("error making UDP address", "err", err)
return
}
client, err := net.ListenMulticastUDP("udp", nil, addr)
if err != nil {
fmt.Printf("Error %v\n", err)
slog.Error("error listening to UDP", "err", err)
return
}
defer client.Close()
@@ -31,13 +31,15 @@ func main() {
buffer := make([]byte, 2048)
length, _, err := client.ReadFromUDP(buffer)
if err != nil {
fmt.Printf("Error %v", err)
slog.Error("error reading from UDP", "err", err)
}
if length > 0 {
decoder.Decode(buffer)
err := decoder.Decode(buffer)
if err != nil {
slog.Error("error decoding", "err", err)
}
}
if time.Now().UnixMilli()-lastPrintMillis > 1000 {
+47 -40
View File
@@ -2,6 +2,7 @@ package main
import (
"fmt"
"log/slog"
"math"
"net"
"time"
@@ -11,7 +12,6 @@ import (
func main() {
client, err := net.Dial("udp", "236.10.10.10:56565")
if err != nil {
fmt.Printf("Error %v\n", err)
return
@@ -23,63 +23,70 @@ func main() {
VersionLow: 0,
}
trackers := []psn.Tracker{}
trackers := []*psn.Tracker{}
trackers = append(trackers, psn.Tracker{Id: 0, Name: "Sun"})
trackers = append(trackers, psn.Tracker{Id: 1, Name: "Mercury"})
trackers = append(trackers, psn.Tracker{Id: 2, Name: "Venus"})
trackers = append(trackers, psn.Tracker{Id: 3, Name: "Earth"})
trackers = append(trackers, psn.Tracker{Id: 4, Name: "Mars"})
trackers = append(trackers, psn.Tracker{Id: 5, Name: "Jupiter"})
trackers = append(trackers, psn.Tracker{Id: 6, Name: "Saturn"})
trackers = append(trackers, psn.Tracker{Id: 7, Name: "Uranus"})
trackers = append(trackers, psn.Tracker{Id: 8, Name: "Neptune"})
trackers = append(trackers, psn.Tracker{Id: 9, Name: "Pluto"})
trackers = append(trackers, &psn.Tracker{Id: 0, Name: "Sun"})
trackers = append(trackers, &psn.Tracker{Id: 1, Name: "Mercury"})
trackers = append(trackers, &psn.Tracker{Id: 2, Name: "Venus"})
trackers = append(trackers, &psn.Tracker{Id: 3, Name: "Earth"})
trackers = append(trackers, &psn.Tracker{Id: 4, Name: "Mars"})
trackers = append(trackers, &psn.Tracker{Id: 5, Name: "Jupiter"})
trackers = append(trackers, &psn.Tracker{Id: 6, Name: "Saturn"})
trackers = append(trackers, &psn.Tracker{Id: 7, Name: "Uranus"})
trackers = append(trackers, &psn.Tracker{Id: 8, Name: "Neptune"})
trackers = append(trackers, &psn.Tracker{Id: 9, Name: "Pluto"})
orbits := []float32{1.0, 88.0, 224.7, 365.2, 687, 4332, 10760, 30700, 60200, 90600}
distFromSun := []float32{0, 0.58, 1.08, 1.5, 2.28, 7.78, 14.29, 28.71, 45.04, 59.13}
timestamp := 0
lastInfoMillis := time.Now().UnixMilli()
lastDataMillis := time.Now().UnixMilli()
dataTicker := time.NewTicker(time.Millisecond * 16)
infoTicker := time.NewTicker(time.Millisecond * 1000)
for {
if (time.Now().UnixMilli() - lastInfoMillis) > 500 {
fmt.Println("Sending Info Packets")
infoPackets := encoder.GetInfoPackets(uint64(timestamp), trackers)
for _, infoPacket := range infoPackets {
client.Write(infoPacket)
}
lastInfoMillis = time.Now().UnixMilli()
}
if (time.Now().UnixMilli() - lastDataMillis) > 5 {
for index, orbit := range orbits {
go func() {
for {
for index, tracker := range trackers {
orbit := orbits[index]
a := 1.0 / orbit
b := distFromSun[index]
x := timestamp
cb := math.Cos(float64(a*float32(x))) * float64(b)
sb := math.Sin(float64(a*float32(x))) * float64(b)
trackers[index].SetPos(float32(sb), 0, float32(cb))
trackers[index].SetSpeed(a*float32(cb), 0, -a*float32(sb))
trackers[index].SetOri(0, float32(x)/1000.0, 0)
trackers[index].SetAccel(-a*a*float32(sb), 0, -a*a*float32(cb))
trackers[index].SetTrgtPos(3, 14, 16)
trackers[index].SetStatus(float32(index) / 10.0)
trackers[index].SetTimestamp(uint64(timestamp))
tracker.SetPos(float32(sb), 0, float32(cb))
tracker.SetSpeed(a*float32(cb), 0, -a*float32(sb))
tracker.SetOri(0, float32(x)/1000.0, 0)
tracker.SetAccel(-a*a*float32(sb), 0, -a*a*float32(cb))
tracker.SetTrgtPos(3, 14, 16)
tracker.SetStatus(float32(index) / 10.0)
tracker.SetTimestamp(uint64(timestamp))
}
time.Sleep(time.Millisecond * 16)
}
}()
fmt.Println("Sending Data Packets")
dataPackets := encoder.GetDataPackets(uint64(timestamp), trackers)
for _, DataPacket := range dataPackets {
client.Write(DataPacket)
for {
select {
case <-infoTicker.C:
slog.Info("Sending Info Packets")
infoPackets := encoder.GetInfoPackets(uint64(timestamp), trackers)
for _, infoPacket := range infoPackets {
_, err := client.Write(infoPacket)
if err != nil {
slog.Error("failed to send info packet", "error", err)
}
}
case <-dataTicker.C:
slog.Info("Sending Data Packets")
dataPackets := encoder.GetDataPackets(uint64(timestamp), trackers)
for _, dataPacket := range dataPackets {
_, err := client.Write(dataPacket)
if err != nil {
slog.Error("failed to send data packet", "error", err)
}
}
lastDataMillis = time.Now().UnixMilli()
timestamp += 1
}
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
module github.com/jwetzell/psn-go
go 1.23.1
go 1.25.3
+12
View File
@@ -0,0 +1,12 @@
package chunks
type ChunkHeader struct {
Id uint16
DataLen uint16
HasSubchunks bool
}
type Chunk struct {
ChunkData []byte
Header ChunkHeader
}
+11
View File
@@ -0,0 +1,11 @@
package chunks
type DataPacketChunkData struct {
PacketHeader *PacketHeaderChunk
TrackerList *DataTrackerListChunk
}
type DataPacketChunk struct {
Data DataPacketChunkData
Chunk Chunk
}
+16
View File
@@ -0,0 +1,16 @@
package chunks
type DataTrackerChunkData struct {
Pos *DataTrackerXYZChunk
Speed *DataTrackerXYZChunk
Ori *DataTrackerXYZChunk
Status *DataTrackerStatusChunk
Accel *DataTrackerXYZChunk
TrgtPos *DataTrackerXYZChunk
Timestamp *DataTrackerTimestampChunk
}
type DataTrackerChunk struct {
Data DataTrackerChunkData
Chunk Chunk
}
@@ -0,0 +1,9 @@
package chunks
type DataTrackerListChunkData struct {
Trackers []DataTrackerChunk
}
type DataTrackerListChunk struct {
Chunk Chunk
Data DataTrackerListChunkData
}
@@ -0,0 +1,10 @@
package chunks
type DataTrackerStatusChunkData struct {
Validity float32
}
type DataTrackerStatusChunk struct {
Chunk Chunk
Data DataTrackerStatusChunkData
}
@@ -0,0 +1,10 @@
package chunks
type DataTrackerTimestampChunkData struct {
Timestamp uint64
}
type DataTrackerTimestampChunk struct {
Chunk Chunk
Data DataTrackerTimestampChunkData
}
+12
View File
@@ -0,0 +1,12 @@
package chunks
type DataTrackerXYZChunkData struct {
X float32
Y float32
Z float32
}
type DataTrackerXYZChunk struct {
Chunk Chunk
Data DataTrackerXYZChunkData
}
+12
View File
@@ -0,0 +1,12 @@
package chunks
type InfoPacketChunkData struct {
PacketHeader *PacketHeaderChunk
SystemName *InfoSystemNameChunk
TrackerList *InfoTrackerListChunk
}
type InfoPacketChunk struct {
Data InfoPacketChunkData
Chunk Chunk
}
+10
View File
@@ -0,0 +1,10 @@
package chunks
type InfoSystemNameChunkData struct {
SystemName string
}
type InfoSystemNameChunk struct {
Data InfoSystemNameChunkData
Chunk Chunk
}
+10
View File
@@ -0,0 +1,10 @@
package chunks
type InfoTrackerChunkData struct {
TrackerName *InfoTrackerNameChunk
}
type InfoTrackerChunk struct {
Data InfoTrackerChunkData
Chunk Chunk
}
@@ -0,0 +1,10 @@
package chunks
type InfoTrackerListChunkData struct {
Trackers []InfoTrackerChunk
}
type InfoTrackerListChunk struct {
Chunk Chunk
Data InfoTrackerListChunkData
}
@@ -0,0 +1,10 @@
package chunks
type InfoTrackerNameChunkData struct {
TrackerName string
}
type InfoTrackerNameChunk struct {
Data InfoTrackerNameChunkData
Chunk Chunk
}
+14
View File
@@ -0,0 +1,14 @@
package chunks
type PacketHeaderChunkData struct {
PacketTimestamp uint64
VersionHigh uint8
VersionLow uint8
FrameId uint8
FramePacketCount uint8
}
type PacketHeaderChunk struct {
Chunk Chunk
Data PacketHeaderChunkData
}
+17 -17
View File
@@ -2,42 +2,42 @@ package decoders
import (
"encoding/binary"
"errors"
"github.com/jwetzell/psn-go/internal/chunks"
)
type ChunkHeader struct {
Id uint16
DataLen uint16
HasSubchunks bool
}
func DecodeChunk(bytes []byte) (chunks.Chunk, error) {
type Chunk struct {
Header ChunkHeader
ChunkData []byte
}
if len(bytes) < 4 {
return chunks.Chunk{}, errors.New("chunk must be at least 4 bytes")
}
func DecodeChunk(bytes []byte) Chunk {
id := binary.LittleEndian.Uint16(bytes[0:2])
lengthAndFlag := binary.LittleEndian.Uint16(bytes[2:4])
data_len := lengthAndFlag
has_subchunks := lengthAndFlag > 32768
has_subchunks := lengthAndFlag >= 32768
if has_subchunks {
data_len = data_len - 32768
}
header := ChunkHeader{
header := chunks.ChunkHeader{
Id: id,
DataLen: data_len,
HasSubchunks: has_subchunks,
}
chunk_data := bytes[4 : 4+header.DataLen]
return Chunk{
Header: header,
ChunkData: chunk_data,
if len(bytes) < 4+int(header.DataLen) {
return chunks.Chunk{}, errors.New("chunk data length is greater than the number of bytes")
}
chunk_data := bytes[4 : 4+header.DataLen]
return chunks.Chunk{
Header: header,
ChunkData: chunk_data,
}, nil
}
+98 -21
View File
@@ -1,60 +1,137 @@
package decoders
import (
"fmt"
"reflect"
"strings"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestChunkDecoding(t *testing.T) {
func TestGoodChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected Chunk
expected chunks.Chunk
}{
{
description: "info packet",
bytes: []byte{0x56, 0x67, 0x34, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
bytes: []byte{
0x56, 0x67, 0x34, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x0b, 0x00, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x02, 0x00, 0x11,
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31},
expected: Chunk{
Header: ChunkHeader{
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31,
},
expected: chunks.Chunk{
Header: chunks.ChunkHeader{
Id: uint16(26454),
DataLen: uint16(52),
HasSubchunks: true,
},
ChunkData: []byte{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
ChunkData: []byte{
0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x0b, 0x00, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x02, 0x00, 0x11,
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31},
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31,
},
},
},
{
description: "data packet",
bytes: []byte{0x55, 0x67, 0x28, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
bytes: []byte{
0x55, 0x67, 0x28, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x14, 0x80, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f},
expected: Chunk{
Header: ChunkHeader{
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
},
expected: chunks.Chunk{
Header: chunks.ChunkHeader{
Id: uint16(26453),
DataLen: uint16(40),
HasSubchunks: true,
},
ChunkData: []byte{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
ChunkData: []byte{
0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x14, 0x80, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f},
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeChunk(testCase.bytes)
actual := DecodeChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("Test '%s' failed to decode chunk properly", testCase.description)
fmt.Printf("expected: %v\n", testCase.expected)
fmt.Printf("actual: %v\n", actual)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
func TestBadChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
errorShouldContain string
}{
{
description: "empty packet",
bytes: []byte{},
errorShouldContain: "must be at least 4 bytes",
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
_, err := DecodeChunk(testCase.bytes)
if err == nil {
t.Error("should have failed fail to decode chunk properly")
}
if !strings.Contains(err.Error(), testCase.errorShouldContain) {
t.Errorf("did not return the correct error expected: %s, got: %s", testCase.errorShouldContain, err.Error())
}
})
}
}
func BenchmarkChunkDecoding(b *testing.B) {
data := []byte{
0x56, 0x67, 0x34, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x0b, 0x00, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x02, 0x00, 0x11,
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31,
}
for b.Loop() {
_, err := DecodeChunk(data)
if err != nil {
b.Errorf("failed to decode chunk properly, error: %v", err)
}
}
}
func FuzzChunkDecoding(f *testing.F) {
seedCases := [][]byte{
{
0x56, 0x67, 0x34, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x0b, 0x00, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x02, 0x00, 0x11,
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31,
},
{
0x55, 0x67, 0x28, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x14, 0x80, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
},
}
for _, seed := range seedCases {
f.Add(seed)
}
f.Fuzz(func(t *testing.T, bytes []byte) {
_, _ = DecodeChunk(bytes)
})
}
+23 -20
View File
@@ -3,37 +3,39 @@ package decoders
import (
"encoding/binary"
"log/slog"
"github.com/jwetzell/psn-go/internal/chunks"
)
type DataPacketChunkData struct {
PacketHeader *PacketHeaderChunk
TrackerList *DataTrackerListChunk
}
func DecodeDataPacketChunk(bytes []byte) (chunks.DataPacketChunk, error) {
chunk, err := DecodeChunk(bytes)
type DataPacketChunk struct {
Chunk Chunk
Data DataPacketChunkData
}
if err != nil {
return chunks.DataPacketChunk{}, err
}
func DecodeDataPacketChunk(bytes []byte) DataPacketChunk {
chunk := DecodeChunk(bytes)
data := DataPacketChunkData{}
data := chunks.DataPacketChunkData{}
if chunk.Header.HasSubchunks && chunk.ChunkData != nil && chunk.Header.DataLen > 0 {
offset := 0
for offset < int(chunk.Header.DataLen) {
switch id := binary.LittleEndian.Uint16(chunk.ChunkData[offset : offset+2]); id {
case 0:
packet_header := DecodePacketHeaderChunk(chunk.ChunkData[offset:])
case 0x0000:
packet_header, err := DecodePacketHeaderChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataPacketChunk{}, err
}
data.PacketHeader = &packet_header
offset += 4
if packet_header.Chunk.Header.DataLen > 0 {
offset = offset + int(packet_header.Chunk.Header.DataLen)
}
case 1:
tracker_list := DecodeDataTrackerListChunk(chunk.ChunkData[offset:])
case 0x0001:
tracker_list, err := DecodeDataTrackerListChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataPacketChunk{}, err
}
data.TrackerList = &tracker_list
offset += 4
if tracker_list.Chunk.Header.DataLen > 0 {
@@ -46,8 +48,9 @@ func DecodeDataPacketChunk(bytes []byte) DataPacketChunk {
}
}
return DataPacketChunk{
Chunk: chunk,
Data: data,
}
return chunks.DataPacketChunk{
Chunk: chunk,
Data: data,
},
nil
}
+169
View File
@@ -0,0 +1,169 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodDataPacketChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.DataPacketChunk
}{
{
description: "DataPacketChunk",
bytes: []byte{
85, 103, 124, 128, 0, 0, 12, 0, 210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123, 1, 0, 104, 128, 1, 0, 100, 128, 0, 0,
12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 2, 0, 12,
0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64,
0, 0, 64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
expected: chunks.DataPacketChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{
0, 0, 12, 0, 210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123, 1, 0, 104, 128, 1, 0, 100, 128, 0, 0, 12, 0, 0, 0,
128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 2, 0, 12, 0, 0, 0,
128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0,
64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
Header: chunks.ChunkHeader{
DataLen: 124, Id: 26453, HasSubchunks: true,
},
},
Data: chunks.DataPacketChunkData{
PacketHeader: &chunks.PacketHeaderChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123},
Header: chunks.ChunkHeader{DataLen: 12, Id: 0, HasSubchunks: false},
},
Data: chunks.PacketHeaderChunkData{
PacketTimestamp: 1234567890,
VersionHigh: 2,
VersionLow: 3,
FrameId: 1,
FramePacketCount: 123,
},
},
TrackerList: &chunks.DataTrackerListChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{
1, 0, 100, 128, 0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0,
0, 64, 0, 0, 64, 64, 2, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4,
0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64,
6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
Header: chunks.ChunkHeader{DataLen: 104, Id: 1, HasSubchunks: true},
},
Data: chunks.DataTrackerListChunkData{
Trackers: []chunks.DataTrackerChunk{
chunks.DataTrackerChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{
0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0,
0, 64, 64, 2, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4, 0,
12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64,
64, 6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
Header: chunks.ChunkHeader{DataLen: 100, Id: 1, HasSubchunks: true},
},
Data: chunks.DataTrackerChunkData{
Pos: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 0, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Speed: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 1, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Ori: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 2, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Status: &chunks.DataTrackerStatusChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63},
Header: chunks.ChunkHeader{DataLen: 4, Id: 3, HasSubchunks: false},
},
Data: chunks.DataTrackerStatusChunkData{
Validity: 1.0,
},
},
Accel: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 4, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
TrgtPos: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 5, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Timestamp: &chunks.DataTrackerTimestampChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{210, 2, 150, 73, 0, 0, 0, 0},
Header: chunks.ChunkHeader{DataLen: 8, Id: 6, HasSubchunks: false},
},
Data: chunks.DataTrackerTimestampChunkData{
Timestamp: 1234567890,
},
},
},
},
},
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeDataPacketChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+48 -35
View File
@@ -3,77 +3,89 @@ package decoders
import (
"encoding/binary"
"log/slog"
"github.com/jwetzell/psn-go/internal/chunks"
)
type DataTrackerChunkData struct {
Pos *DataTrackerXYZChunk
Speed *DataTrackerXYZChunk
Ori *DataTrackerXYZChunk
Status *DataTrackerStatusChunk
Accel *DataTrackerXYZChunk
TrgtPos *DataTrackerXYZChunk
Timestamp *DataTrackerTimestampChunk
}
func DecodeDataTrackerChunk(bytes []byte) (chunks.DataTrackerChunk, error) {
chunk, err := DecodeChunk(bytes)
type DataTrackerChunk struct {
Chunk Chunk
Data DataTrackerChunkData
}
if err != nil {
return chunks.DataTrackerChunk{}, err
}
func DecodeDataTrackerChunk(bytes []byte) DataTrackerChunk {
chunk := DecodeChunk(bytes)
data := DataTrackerChunkData{}
data := chunks.DataTrackerChunkData{}
if chunk.Header.HasSubchunks && chunk.ChunkData != nil && chunk.Header.DataLen > 0 {
offset := 0
for offset < int(chunk.Header.DataLen) {
switch id := binary.LittleEndian.Uint16(chunk.ChunkData[offset : offset+2]); id {
case 0:
pos := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
case 0x0000:
pos, err := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataTrackerChunk{}, err
}
data.Pos = &pos
offset += 4
if data.Pos.Chunk.Header.DataLen > 0 {
offset = offset + int(data.Pos.Chunk.Header.DataLen)
}
case 1:
speed := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
case 0x0001:
speed, err := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataTrackerChunk{}, err
}
data.Speed = &speed
offset += 4
if data.Speed.Chunk.Header.DataLen > 0 {
offset = offset + int(data.Speed.Chunk.Header.DataLen)
}
case 2:
ori := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
case 0x0002:
ori, err := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataTrackerChunk{}, err
}
data.Ori = &ori
offset += 4
if data.Ori.Chunk.Header.DataLen > 0 {
offset = offset + int(data.Ori.Chunk.Header.DataLen)
}
case 3:
status := DecodeDataTrackerStatusChunk(chunk.ChunkData[offset:])
case 0x0003:
status, err := DecodeDataTrackerStatusChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataTrackerChunk{}, err
}
data.Status = &status
offset += 4
if data.Status.Chunk.Header.DataLen > 0 {
offset = offset + int(data.Status.Chunk.Header.DataLen)
}
case 4:
accel := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
case 0x0004:
accel, err := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataTrackerChunk{}, err
}
data.Accel = &accel
offset += 4
if data.Accel.Chunk.Header.DataLen > 0 {
offset = offset + int(data.Accel.Chunk.Header.DataLen)
}
case 5:
trgtpos := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
case 0x0005:
trgtpos, err := DecodeDataTrackerXYZChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataTrackerChunk{}, err
}
data.TrgtPos = &trgtpos
offset += 4
if data.TrgtPos.Chunk.Header.DataLen > 0 {
offset = offset + int(data.TrgtPos.Chunk.Header.DataLen)
}
case 6:
timestamp := DecodeDataTrackerTimestampChunk(chunk.ChunkData[offset:])
case 0x0006:
timestamp, err := DecodeDataTrackerTimestampChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataTrackerChunk{}, err
}
data.Timestamp = &timestamp
offset += 4
if data.Timestamp.Chunk.Header.DataLen > 0 {
@@ -86,8 +98,9 @@ func DecodeDataTrackerChunk(bytes []byte) DataTrackerChunk {
}
}
return DataTrackerChunk{
Chunk: chunk,
Data: data,
}
return chunks.DataTrackerChunk{
Chunk: chunk,
Data: data,
},
nil
}
@@ -0,0 +1,125 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodDataTrackerChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.DataTrackerChunk
}{
{
description: "DataTracker",
bytes: []byte{
1, 0, 100, 128, 0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 2, 0, 12,
0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64,
0, 0, 64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
expected: chunks.DataTrackerChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{
0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0,
0, 64, 64, 2, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4, 0,
12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64,
64, 6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
Header: chunks.ChunkHeader{DataLen: 100, Id: 1, HasSubchunks: true},
},
Data: chunks.DataTrackerChunkData{
Pos: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 0, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Speed: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 1, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Ori: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 2, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Status: &chunks.DataTrackerStatusChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63},
Header: chunks.ChunkHeader{DataLen: 4, Id: 3, HasSubchunks: false},
},
Data: chunks.DataTrackerStatusChunkData{
Validity: 1.0,
},
},
Accel: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 4, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
TrgtPos: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 5, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Timestamp: &chunks.DataTrackerTimestampChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{210, 2, 150, 73, 0, 0, 0, 0},
Header: chunks.ChunkHeader{DataLen: 8, Id: 6, HasSubchunks: false},
},
Data: chunks.DataTrackerTimestampChunkData{
Timestamp: 1234567890,
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeDataTrackerChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+18 -17
View File
@@ -1,21 +1,22 @@
package decoders
type DataTrackerListChunkData struct {
Trackers []DataTrackerChunk
}
type DataTrackerListChunk struct {
Chunk Chunk
Data DataTrackerListChunkData
}
import "github.com/jwetzell/psn-go/internal/chunks"
func DecodeDataTrackerListChunk(bytes []byte) DataTrackerListChunk {
chunk := DecodeChunk(bytes)
func DecodeDataTrackerListChunk(bytes []byte) (chunks.DataTrackerListChunk, error) {
chunk, err := DecodeChunk(bytes)
trackers := []DataTrackerChunk{}
if err != nil {
return chunks.DataTrackerListChunk{}, err
}
trackers := []chunks.DataTrackerChunk{}
if chunk.Header.HasSubchunks && chunk.Header.DataLen > 0 {
offset := 0
for offset < int(chunk.Header.DataLen) {
trackerChunk := DecodeDataTrackerChunk(chunk.ChunkData[offset:])
trackerChunk, err := DecodeDataTrackerChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.DataTrackerListChunk{}, err
}
offset += 4
if trackerChunk.Chunk.Header.DataLen > 0 {
offset += int(trackerChunk.Chunk.Header.DataLen)
@@ -24,13 +25,13 @@ func DecodeDataTrackerListChunk(bytes []byte) DataTrackerListChunk {
}
}
data := DataTrackerListChunkData{
data := chunks.DataTrackerListChunkData{
Trackers: trackers,
}
return DataTrackerListChunk{
Chunk: chunk,
Data: data,
}
return chunks.DataTrackerListChunk{
Chunk: chunk,
Data: data,
},
nil
}
@@ -0,0 +1,140 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodDataTrackerListChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.DataTrackerListChunk
}{
{
description: "DataTrackerListChunk",
bytes: []byte{
1, 0, 104, 128, 1, 0, 100, 128, 0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0,
64, 0, 0, 64, 64, 2, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4, 0, 12, 0, 0, 0, 128,
63, 0, 0, 0, 64, 0, 0, 64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
expected: chunks.DataTrackerListChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{
1, 0, 100, 128, 0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0,
0, 64, 0, 0, 64, 64, 2, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4,
0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64,
6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
Header: chunks.ChunkHeader{DataLen: 104, Id: 1, HasSubchunks: true},
},
Data: chunks.DataTrackerListChunkData{
Trackers: []chunks.DataTrackerChunk{
{
Chunk: chunks.Chunk{
ChunkData: []byte{
0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0,
0, 64, 64, 2, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 3, 0, 4, 0, 0, 0, 128, 63, 4, 0,
12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64,
64, 6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
Header: chunks.ChunkHeader{DataLen: 100, Id: 1, HasSubchunks: true},
},
Data: chunks.DataTrackerChunkData{
Pos: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 0, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Speed: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 1, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Ori: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 2, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Status: &chunks.DataTrackerStatusChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63},
Header: chunks.ChunkHeader{DataLen: 4, Id: 3, HasSubchunks: false},
},
Data: chunks.DataTrackerStatusChunkData{
Validity: 1.0,
},
},
Accel: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 4, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
TrgtPos: &chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 5, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
Timestamp: &chunks.DataTrackerTimestampChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{210, 2, 150, 73, 0, 0, 0, 0},
Header: chunks.ChunkHeader{DataLen: 8, Id: 6, HasSubchunks: false},
},
Data: chunks.DataTrackerTimestampChunkData{
Timestamp: 1234567890,
},
},
},
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeDataTrackerListChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+17 -15
View File
@@ -2,30 +2,32 @@ package decoders
import (
"encoding/binary"
"errors"
"math"
"github.com/jwetzell/psn-go/internal/chunks"
)
type DataTrackerStatusChunkData struct {
Validity float32
}
func DecodeDataTrackerStatusChunk(bytes []byte) (chunks.DataTrackerStatusChunk, error) {
chunk, err := DecodeChunk(bytes)
type DataTrackerStatusChunk struct {
Chunk Chunk
Data DataTrackerStatusChunkData
}
if err != nil {
return chunks.DataTrackerStatusChunk{}, err
}
func DecodeDataTrackerStatusChunk(bytes []byte) DataTrackerStatusChunk {
chunk := DecodeChunk(bytes)
if len(chunk.ChunkData) < 4 {
return chunks.DataTrackerStatusChunk{}, errors.New("DATA_TRACKER_STATUS chunk must be at least 4 bytes")
}
statusBits := binary.LittleEndian.Uint32(chunk.ChunkData[0:4])
data := DataTrackerStatusChunkData{
data := chunks.DataTrackerStatusChunkData{
Validity: math.Float32frombits(statusBits),
}
return DataTrackerStatusChunk{
Chunk: chunk,
Data: data,
}
return chunks.DataTrackerStatusChunk{
Chunk: chunk,
Data: data,
},
nil
}
@@ -0,0 +1,46 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodDataTrackerStatusChunk(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.DataTrackerStatusChunk
}{
{
description: "DataTrackerStatusChunk",
bytes: []byte{3, 0, 4, 0, 0, 0, 128, 63},
expected: chunks.DataTrackerStatusChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63},
Header: chunks.ChunkHeader{DataLen: 4, Id: 3, HasSubchunks: false},
},
Data: chunks.DataTrackerStatusChunkData{
Validity: 1.0,
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeDataTrackerStatusChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
@@ -2,29 +2,31 @@ package decoders
import (
"encoding/binary"
"errors"
"github.com/jwetzell/psn-go/internal/chunks"
)
type DataTrackerTimestampChunkData struct {
Timestamp uint64
}
func DecodeDataTrackerTimestampChunk(bytes []byte) (chunks.DataTrackerTimestampChunk, error) {
chunk, err := DecodeChunk(bytes)
type DataTrackerTimestampChunk struct {
Chunk Chunk
Data DataTrackerTimestampChunkData
}
if err != nil {
return chunks.DataTrackerTimestampChunk{}, err
}
func DecodeDataTrackerTimestampChunk(bytes []byte) DataTrackerTimestampChunk {
chunk := DecodeChunk(bytes)
if len(chunk.ChunkData) < 8 {
return chunks.DataTrackerTimestampChunk{}, errors.New("DATA_TRACKER_TIMESTAMP chunk must be at least 8 bytes")
}
timestamp := binary.LittleEndian.Uint64(chunk.ChunkData[0:8])
data := DataTrackerTimestampChunkData{
data := chunks.DataTrackerTimestampChunkData{
Timestamp: timestamp,
}
return DataTrackerTimestampChunk{
Chunk: chunk,
Data: data,
}
return chunks.DataTrackerTimestampChunk{
Chunk: chunk,
Data: data,
},
nil
}
@@ -0,0 +1,47 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodDataTrackerTimestampChunk(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.DataTrackerTimestampChunk
}{
{
description: "DataTrackerTimestampChunk",
bytes: []byte{6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0},
expected: chunks.DataTrackerTimestampChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{210, 2, 150, 73, 0, 0, 0, 0},
Header: chunks.ChunkHeader{DataLen: 8, Id: 6, HasSubchunks: false},
},
Data: chunks.DataTrackerTimestampChunkData{
Timestamp: 1234567890,
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeDataTrackerTimestampChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+14 -15
View File
@@ -2,36 +2,35 @@ package decoders
import (
"encoding/binary"
"errors"
"math"
"github.com/jwetzell/psn-go/internal/chunks"
)
type DataTrackerXYZChunkData struct {
X float32
Y float32
Z float32
}
func DecodeDataTrackerXYZChunk(bytes []byte) (chunks.DataTrackerXYZChunk, error) {
chunk, err := DecodeChunk(bytes)
type DataTrackerXYZChunk struct {
Chunk Chunk
Data DataTrackerXYZChunkData
}
if err != nil {
return chunks.DataTrackerXYZChunk{}, err
}
func DecodeDataTrackerXYZChunk(bytes []byte) DataTrackerXYZChunk {
chunk := DecodeChunk(bytes)
if len(chunk.ChunkData) < 12 {
return chunks.DataTrackerXYZChunk{}, errors.New("DATA_TRACKER_XYZ chunk must be at least 12 bytes")
}
xBits := binary.LittleEndian.Uint32(chunk.ChunkData[0:4])
yBits := binary.LittleEndian.Uint32(chunk.ChunkData[4:8])
zBits := binary.LittleEndian.Uint32(chunk.ChunkData[8:12])
data := DataTrackerXYZChunkData{
data := chunks.DataTrackerXYZChunkData{
X: math.Float32frombits(xBits),
Y: math.Float32frombits(yBits),
Z: math.Float32frombits(zBits),
}
return DataTrackerXYZChunk{
return chunks.DataTrackerXYZChunk{
Chunk: chunk,
Data: data,
}
}, nil
}
@@ -0,0 +1,108 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodDataTrackerXYZChunk(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.DataTrackerXYZChunk
}{
{
description: "DataTrackerPosChunk",
bytes: []byte{0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
expected: chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 0, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
},
{
description: "DataTrackerSpeedChunk",
bytes: []byte{1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
expected: chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 1, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
},
{
description: "DataTrackerOriChunk",
bytes: []byte{2, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
expected: chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 2, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
},
{
description: "DataTrackerAccelChunk",
bytes: []byte{4, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
expected: chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 4, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
},
{
description: "DataTrackerTrgtPosChunk",
bytes: []byte{5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
expected: chunks.DataTrackerXYZChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64},
Header: chunks.ChunkHeader{DataLen: 12, Id: 5, HasSubchunks: false},
},
Data: chunks.DataTrackerXYZChunkData{
X: 1.0,
Y: 2.0,
Z: 3.0,
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeDataTrackerXYZChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+28 -25
View File
@@ -3,45 +3,47 @@ package decoders
import (
"encoding/binary"
"log/slog"
"github.com/jwetzell/psn-go/internal/chunks"
)
type InfoPacketChunkData struct {
PacketHeader *PacketHeaderChunk
SystemName *InfoSystemNameChunk
TrackerList *InfoTrackerListChunk
}
type InfoPacketChunk struct {
Chunk Chunk
Data InfoPacketChunkData
}
func DecodeInfoPacketChunk(bytes []byte) InfoPacketChunk {
chunk := DecodeChunk(bytes)
data := InfoPacketChunkData{}
func DecodeInfoPacketChunk(bytes []byte) (chunks.InfoPacketChunk, error) {
chunk, err := DecodeChunk(bytes)
if err != nil {
return chunks.InfoPacketChunk{}, err
}
data := chunks.InfoPacketChunkData{}
if chunk.Header.HasSubchunks && chunk.ChunkData != nil && chunk.Header.DataLen > 0 {
offset := 0
for offset < int(chunk.Header.DataLen) {
switch id := binary.LittleEndian.Uint16(chunk.ChunkData[offset : offset+2]); id {
case 0:
packet_header := DecodePacketHeaderChunk(chunk.ChunkData[offset:])
case 0x0000:
packet_header, err := DecodePacketHeaderChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.InfoPacketChunk{}, err
}
data.PacketHeader = &packet_header
offset += 4
if packet_header.Chunk.Header.DataLen > 0 {
offset = offset + int(packet_header.Chunk.Header.DataLen)
}
case 1:
system_name := DecodeInfoSystemNameChunk(chunk.ChunkData[offset:])
case 0x0001:
system_name, err := DecodeInfoSystemNameChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.InfoPacketChunk{}, err
}
data.SystemName = &system_name
offset += 4
if system_name.Chunk.Header.DataLen > 0 {
offset = offset + int(system_name.Chunk.Header.DataLen)
}
case 2:
tracker_list := DecodeInfoTrackerListChunk(chunk.ChunkData[offset:])
case 0x0002:
tracker_list, err := DecodeInfoTrackerListChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.InfoPacketChunk{}, err
}
data.TrackerList = &tracker_list
offset += 4
if tracker_list.Chunk.Header.DataLen > 0 {
@@ -54,8 +56,9 @@ func DecodeInfoPacketChunk(bytes []byte) InfoPacketChunk {
}
}
return InfoPacketChunk{
Chunk: chunk,
Data: data,
}
return chunks.InfoPacketChunk{
Chunk: chunk,
Data: data,
},
nil
}
+148
View File
@@ -0,0 +1,148 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodInfoPacketChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.InfoPacketChunk
}{
{
description: "InfoPacketChunk",
bytes: []byte{
86, 103, 51, 128, 0, 0, 12, 0, 210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123, 1, 0, 10, 0, 80, 83, 78, 32, 83, 101,
114, 118, 101, 114, 2, 0, 17, 128, 1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49,
},
expected: chunks.InfoPacketChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{
0, 0, 12, 0, 210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123, 1, 0, 10, 0, 80, 83, 78, 32, 83, 101, 114, 118, 101,
114, 2, 0, 17, 128, 1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49,
},
Header: chunks.ChunkHeader{DataLen: 51, Id: 26454, HasSubchunks: true},
},
Data: chunks.InfoPacketChunkData{
PacketHeader: &chunks.PacketHeaderChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123},
Header: chunks.ChunkHeader{DataLen: 12, Id: 0, HasSubchunks: false},
},
Data: chunks.PacketHeaderChunkData{
PacketTimestamp: 1234567890,
VersionHigh: 2,
VersionLow: 3,
FrameId: 1,
FramePacketCount: 123,
},
},
SystemName: &chunks.InfoSystemNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{80, 83, 78, 32, 83, 101, 114, 118, 101, 114},
Header: chunks.ChunkHeader{DataLen: 10, Id: 1, HasSubchunks: false},
},
Data: chunks.InfoSystemNameChunkData{
SystemName: "PSN Server",
},
},
TrackerList: &chunks.InfoTrackerListChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 17, Id: 2, HasSubchunks: true},
},
Data: chunks.InfoTrackerListChunkData{
Trackers: []chunks.InfoTrackerChunk{
{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 13, Id: 1, HasSubchunks: true},
},
Data: chunks.InfoTrackerChunkData{
TrackerName: &chunks.InfoTrackerNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 9, Id: 0, HasSubchunks: false},
},
Data: chunks.InfoTrackerNameChunkData{
TrackerName: "Tracker 1",
},
},
},
},
},
},
},
},
},
},
{
description: "empty InfoPacketChunk",
bytes: []byte{
0x56, 0x67, 0x22, 0x80, 0x00, 0x00, 0x0C, 0x00, 0x3D, 0x2C, 0x91, 0xDB, 0x9A, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00,
0x01, 0x01, 0x00, 0x0A, 0x00, 0x50, 0x53, 0x4E, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x02, 0x00, 0x00, 0x80,
},
expected: chunks.InfoPacketChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{
0x00, 0x00, 0x0C, 0x00, 0x3D, 0x2C, 0x91, 0xDB, 0x9A, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00,
0x01, 0x01, 0x00, 0x0A, 0x00, 0x50, 0x53, 0x4E, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x02, 0x00, 0x00, 0x80,
},
Header: chunks.ChunkHeader{DataLen: 34, Id: 26454, HasSubchunks: true},
},
Data: chunks.InfoPacketChunkData{
PacketHeader: &chunks.PacketHeaderChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0x3D, 0x2C, 0x91, 0xDB, 0x9A, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01},
Header: chunks.ChunkHeader{DataLen: 12, Id: 0, HasSubchunks: false},
},
Data: chunks.PacketHeaderChunkData{
PacketTimestamp: 1764620315709,
VersionHigh: 2,
VersionLow: 0,
FrameId: 0,
FramePacketCount: 1,
},
},
SystemName: &chunks.InfoSystemNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{80, 83, 78, 32, 83, 101, 114, 118, 101, 114},
Header: chunks.ChunkHeader{DataLen: 10, Id: 1, HasSubchunks: false},
},
Data: chunks.InfoSystemNameChunkData{
SystemName: "PSN Server",
},
},
TrackerList: &chunks.InfoTrackerListChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{},
Header: chunks.ChunkHeader{DataLen: 0, Id: 2, HasSubchunks: true},
},
Data: chunks.InfoTrackerListChunkData{
Trackers: []chunks.InfoTrackerChunk{},
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeInfoPacketChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+11 -16
View File
@@ -1,25 +1,20 @@
package decoders
type InfoSystemNameChunkData struct {
SystemName string
}
import "github.com/jwetzell/psn-go/internal/chunks"
type InfoSystemNameChunk struct {
Chunk Chunk
Data InfoSystemNameChunkData
}
func DecodeInfoSystemNameChunk(bytes []byte) (chunks.InfoSystemNameChunk, error) {
chunk, err := DecodeChunk(bytes)
if err != nil {
return chunks.InfoSystemNameChunk{}, err
}
data := chunks.InfoSystemNameChunkData{}
func DecodeInfoSystemNameChunk(bytes []byte) InfoSystemNameChunk {
chunk := DecodeChunk(bytes)
system_name := string(chunk.ChunkData[0:chunk.Header.DataLen])
data := InfoSystemNameChunkData{
SystemName: system_name,
if chunk.Header.DataLen > 0 {
data.SystemName = string(chunk.ChunkData[0:chunk.Header.DataLen])
}
return InfoSystemNameChunk{
return chunks.InfoSystemNameChunk{
Chunk: chunk,
Data: data,
}
}, nil
}
@@ -0,0 +1,46 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodInfoSystemNameChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.InfoSystemNameChunk
}{
{
description: "InfoSystemNameChunk",
bytes: []byte{
1, 0, 10, 0, 80, 83, 78, 32, 83, 101, 114, 118, 101, 114,
},
expected: chunks.InfoSystemNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{80, 83, 78, 32, 83, 101, 114, 118, 101, 114},
Header: chunks.ChunkHeader{DataLen: 10, Id: 1, HasSubchunks: false},
},
Data: chunks.InfoSystemNameChunkData{
SystemName: "PSN Server",
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeInfoSystemNameChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+18 -19
View File
@@ -3,29 +3,27 @@ package decoders
import (
"encoding/binary"
"log/slog"
"github.com/jwetzell/psn-go/internal/chunks"
)
type InfoTrackerChunkData struct {
TrackerName *InfoTrackerNameChunk
}
type InfoTrackerChunk struct {
Chunk Chunk
Data InfoTrackerChunkData
}
func DecodeInfoTrackerChunk(bytes []byte) InfoTrackerChunk {
chunk := DecodeChunk(bytes)
data := InfoTrackerChunkData{}
func DecodeInfoTrackerChunk(bytes []byte) (chunks.InfoTrackerChunk, error) {
chunk, err := DecodeChunk(bytes)
if err != nil {
return chunks.InfoTrackerChunk{}, err
}
data := chunks.InfoTrackerChunkData{}
if chunk.Header.HasSubchunks && chunk.ChunkData != nil && chunk.Header.DataLen > 0 {
offset := 0
for offset < int(chunk.Header.DataLen) {
switch id := binary.LittleEndian.Uint16(chunk.ChunkData[offset : offset+2]); id {
case 0:
tracker_name := DecodeInfoTrackerNameChunk(chunk.ChunkData[offset:])
case 0x0000:
tracker_name, err := DecodeInfoTrackerNameChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.InfoTrackerChunk{}, err
}
data.TrackerName = &tracker_name
offset += 4
if tracker_name.Chunk.Header.DataLen > 0 {
@@ -37,8 +35,9 @@ func DecodeInfoTrackerChunk(bytes []byte) InfoTrackerChunk {
}
}
return InfoTrackerChunk{
Chunk: chunk,
Data: data,
}
return chunks.InfoTrackerChunk{
Chunk: chunk,
Data: data,
},
nil
}
@@ -0,0 +1,54 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodInfoTrackerChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.InfoTrackerChunk
}{
{
description: "InfoTrackerChunk",
bytes: []byte{
1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49,
},
expected: chunks.InfoTrackerChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 13, Id: 1, HasSubchunks: true},
},
Data: chunks.InfoTrackerChunkData{
TrackerName: &chunks.InfoTrackerNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 9, Id: 0, HasSubchunks: false},
},
Data: chunks.InfoTrackerNameChunkData{
TrackerName: "Tracker 1",
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeInfoTrackerChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+17 -18
View File
@@ -1,22 +1,21 @@
package decoders
type InfoTrackerListChunkData struct {
Trackers []InfoTrackerChunk
}
import "github.com/jwetzell/psn-go/internal/chunks"
type InfoTrackerListChunk struct {
Chunk Chunk
Data InfoTrackerListChunkData
}
func DecodeInfoTrackerListChunk(bytes []byte) (chunks.InfoTrackerListChunk, error) {
chunk, err := DecodeChunk(bytes)
if err != nil {
return chunks.InfoTrackerListChunk{}, err
}
func DecodeInfoTrackerListChunk(bytes []byte) InfoTrackerListChunk {
chunk := DecodeChunk(bytes)
trackers := []InfoTrackerChunk{}
trackers := []chunks.InfoTrackerChunk{}
if chunk.Header.HasSubchunks && chunk.Header.DataLen > 0 {
offset := 0
for offset < int(chunk.Header.DataLen) {
trackerChunk := DecodeInfoTrackerChunk(chunk.ChunkData[offset:])
trackerChunk, err := DecodeInfoTrackerChunk(chunk.ChunkData[offset:])
if err != nil {
return chunks.InfoTrackerListChunk{}, err
}
offset += 4
if trackerChunk.Chunk.Header.DataLen > 0 {
offset += int(trackerChunk.Chunk.Header.DataLen)
@@ -25,13 +24,13 @@ func DecodeInfoTrackerListChunk(bytes []byte) InfoTrackerListChunk {
}
}
data := InfoTrackerListChunkData{
data := chunks.InfoTrackerListChunkData{
Trackers: trackers,
}
return InfoTrackerListChunk{
Chunk: chunk,
Data: data,
}
return chunks.InfoTrackerListChunk{
Chunk: chunk,
Data: data,
},
nil
}
@@ -0,0 +1,64 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodInfoTrackerListChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.InfoTrackerListChunk
}{
{
description: "InfoTrackerListChunk",
bytes: []byte{
2, 0, 17, 128, 1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49,
},
expected: chunks.InfoTrackerListChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 17, Id: 2, HasSubchunks: true},
},
Data: chunks.InfoTrackerListChunkData{
Trackers: []chunks.InfoTrackerChunk{
{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 13, Id: 1, HasSubchunks: true},
},
Data: chunks.InfoTrackerChunkData{
TrackerName: &chunks.InfoTrackerNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 9, Id: 0, HasSubchunks: false},
},
Data: chunks.InfoTrackerNameChunkData{
TrackerName: "Tracker 1",
},
},
},
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeInfoTrackerListChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+14 -17
View File
@@ -1,26 +1,23 @@
package decoders
type InfoTrackerNameChunkData struct {
TrackerName string
}
import "github.com/jwetzell/psn-go/internal/chunks"
type InfoTrackerNameChunk struct {
Chunk Chunk
Data InfoTrackerNameChunkData
}
func DecodeInfoTrackerNameChunk(bytes []byte) (chunks.InfoTrackerNameChunk, error) {
chunk, err := DecodeChunk(bytes)
func DecodeInfoTrackerNameChunk(bytes []byte) InfoTrackerNameChunk {
chunk := DecodeChunk(bytes)
tracker_name := string(chunk.ChunkData[0:chunk.Header.DataLen])
data := InfoTrackerNameChunkData{
TrackerName: tracker_name,
if err != nil {
return chunks.InfoTrackerNameChunk{}, err
}
return InfoTrackerNameChunk{
Chunk: chunk,
Data: data,
data := chunks.InfoTrackerNameChunkData{}
if chunk.Header.DataLen > 0 {
data.TrackerName = string(chunk.ChunkData[0:chunk.Header.DataLen])
}
return chunks.InfoTrackerNameChunk{
Chunk: chunk,
Data: data,
},
nil
}
@@ -0,0 +1,47 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodInfoTrackerNameChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.InfoTrackerNameChunk
}{
{
description: "InfoTrackerNameChunk",
bytes: []byte{
0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49,
},
expected: chunks.InfoTrackerNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 9, Id: 0, HasSubchunks: false},
},
Data: chunks.InfoTrackerNameChunkData{
TrackerName: "Tracker 1",
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodeInfoTrackerNameChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
+15 -21
View File
@@ -1,22 +1,16 @@
package decoders
import "encoding/binary"
import (
"encoding/binary"
type PacketHeaderChunkData struct {
PacketTimestamp uint64
VersionHigh uint8
VersionLow uint8
FrameId uint8
FramePacketCount uint8
}
"github.com/jwetzell/psn-go/internal/chunks"
)
type PacketHeaderChunk struct {
Chunk Chunk
Data PacketHeaderChunkData
}
func DecodePacketHeaderChunk(bytes []byte) PacketHeaderChunk {
chunk := DecodeChunk(bytes)
func DecodePacketHeaderChunk(bytes []byte) (chunks.PacketHeaderChunk, error) {
chunk, err := DecodeChunk(bytes)
if err != nil {
return chunks.PacketHeaderChunk{}, err
}
packet_timestamp := binary.LittleEndian.Uint64(chunk.ChunkData[0:8])
version_high := chunk.ChunkData[8]
@@ -24,7 +18,7 @@ func DecodePacketHeaderChunk(bytes []byte) PacketHeaderChunk {
frame_id := chunk.ChunkData[10]
frame_packet_count := chunk.ChunkData[11]
data := PacketHeaderChunkData{
data := chunks.PacketHeaderChunkData{
PacketTimestamp: packet_timestamp,
VersionHigh: version_high,
VersionLow: version_low,
@@ -32,9 +26,9 @@ func DecodePacketHeaderChunk(bytes []byte) PacketHeaderChunk {
FramePacketCount: frame_packet_count,
}
return PacketHeaderChunk{
Chunk: chunk,
Data: data,
}
return chunks.PacketHeaderChunk{
Chunk: chunk,
Data: data,
},
nil
}
@@ -0,0 +1,50 @@
package decoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestGoodPacketHeaderChunkDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
expected chunks.PacketHeaderChunk
}{
{
description: "PacketHeaderChunk",
bytes: []byte{
0, 0, 12, 0, 210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123,
},
expected: chunks.PacketHeaderChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123},
Header: chunks.ChunkHeader{DataLen: 12, Id: 0, HasSubchunks: false},
},
Data: chunks.PacketHeaderChunkData{
PacketTimestamp: 1234567890,
VersionHigh: 2,
VersionLow: 3,
FrameId: 1,
FramePacketCount: 123,
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual, err := DecodePacketHeaderChunk(testCase.bytes)
if err != nil {
t.Errorf("failed to decode chunk properly, error: %v", err)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to decode chunk properly, expected: %+v, actual: %+v", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("0000")
-1
View File
@@ -3,7 +3,6 @@ package encoders
import "encoding/binary"
func EncodeChunk(id uint16, chunkData []byte, hasSubchunks bool) []byte {
header := []byte{}
header = binary.LittleEndian.AppendUint16(header, id)
+85
View File
@@ -0,0 +1,85 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
chunk chunks.Chunk
}{
{
description: "info packet",
expected: []byte{
0x56, 0x67, 0x34, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x0b, 0x00, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x02, 0x00, 0x11,
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31,
},
chunk: chunks.Chunk{
Header: chunks.ChunkHeader{
Id: uint16(26454),
DataLen: uint16(52),
HasSubchunks: true,
},
ChunkData: []byte{
0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x0b, 0x00, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x02, 0x00, 0x11,
0x80, 0x01, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x09, 0x00, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x31,
},
},
},
{
description: "data packet",
expected: []byte{
0x55, 0x67, 0x28, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x14, 0x80, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
},
chunk: chunks.Chunk{
Header: chunks.ChunkHeader{
Id: uint16(26453),
DataLen: uint16(40),
HasSubchunks: true,
},
ChunkData: []byte{
0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x14, 0x80, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeChunk(testCase.chunk.Header.Id, testCase.chunk.ChunkData, testCase.chunk.Header.HasSubchunks)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
func BenchmarkChunkEncoding(b *testing.B) {
chunk := chunks.Chunk{
Header: chunks.ChunkHeader{
Id: uint16(26453),
DataLen: uint16(40),
HasSubchunks: true,
},
ChunkData: []byte{
0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01,
0x01, 0x01, 0x00, 0x14, 0x80, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
},
}
for b.Loop() {
EncodeChunk(chunk.Header.Id, chunk.ChunkData, chunk.Header.HasSubchunks)
}
}
@@ -0,0 +1,38 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestDataTrackerAccelChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.DataTrackerXYZChunkData
}{
{
description: "basic acceleration",
expected: []byte{
4, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64,
},
data: chunks.DataTrackerXYZChunkData{
X: 1,
Y: 2,
Z: 3,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeDataTrackerAccelChunk(testCase.data.X, testCase.data.Y, testCase.data.Z)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,38 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestDataTrackerOriChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.DataTrackerXYZChunkData
}{
{
description: "basic orientation",
expected: []byte{
2, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64,
},
data: chunks.DataTrackerXYZChunkData{
X: 1,
Y: 2,
Z: 3,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeDataTrackerOriChunk(testCase.data.X, testCase.data.Y, testCase.data.Z)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,38 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestDataTrackerPosChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.DataTrackerXYZChunkData
}{
{
description: "basic position",
expected: []byte{
0, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64,
},
data: chunks.DataTrackerXYZChunkData{
X: 1,
Y: 2,
Z: 3,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeDataTrackerPosChunk(testCase.data.X, testCase.data.Y, testCase.data.Z)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,38 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestDataTrackerSpeedChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.DataTrackerXYZChunkData
}{
{
description: "basic speed",
expected: []byte{
1, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64,
},
data: chunks.DataTrackerXYZChunkData{
X: 1,
Y: 2,
Z: 3,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeDataTrackerSpeedChunk(testCase.data.X, testCase.data.Y, testCase.data.Z)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,36 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestDataTrackerStatusChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.DataTrackerStatusChunkData
}{
{
description: "basic status",
expected: []byte{
3, 0, 4, 0, 0, 0, 128, 63,
},
data: chunks.DataTrackerStatusChunkData{
Validity: 1,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeDataTrackerStatusChunk(testCase.data.Validity)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,36 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestDataTrackerTimestampChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.DataTrackerTimestampChunkData
}{
{
description: "basic timestamp",
expected: []byte{
6, 0, 8, 0, 210, 2, 150, 73, 0, 0, 0, 0,
},
data: chunks.DataTrackerTimestampChunkData{
Timestamp: 1234567890,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeDataTrackerTimestampChunk(testCase.data.Timestamp)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,39 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestDataTrackerTrgtPosChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.DataTrackerXYZChunkData
}{
{
description: "basic target position",
expected: []byte{
5, 0, 12, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64,
},
data: chunks.DataTrackerXYZChunkData{
X: 1,
Y: 2,
Z: 3,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeDataTrackerTrgtPosChunk(testCase.data.X, testCase.data.Y, testCase.data.Z)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,36 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestInfoSystemNameChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.InfoSystemNameChunkData
}{
{
description: "InfoSystemNameChunk",
expected: []byte{
1, 0, 10, 0, 80, 83, 78, 32, 83, 101, 114, 118, 101, 114,
},
data: chunks.InfoSystemNameChunkData{
SystemName: "PSN Server",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeInfoSystemNameChunk(testCase.data.SystemName)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,50 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestInfoTrackerChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
chunk chunks.InfoTrackerChunk
}{
{
description: "InfoTrackerChunk",
expected: []byte{
1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49,
},
chunk: chunks.InfoTrackerChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 13, Id: 1, HasSubchunks: true},
},
Data: chunks.InfoTrackerChunkData{
TrackerName: &chunks.InfoTrackerNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 9, Id: 0, HasSubchunks: false},
},
Data: chunks.InfoTrackerNameChunkData{
TrackerName: "Tracker 1",
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeInfoTrackerChunk(testCase.chunk.Chunk.Header.Id, EncodeInfoTrackerNameChunk(testCase.chunk.Data.TrackerName.Data.TrackerName))
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,65 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestInfoTrackerListChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
chunk chunks.InfoTrackerListChunk
}{
{
description: "InfoTrackerListChunk",
expected: []byte{
2, 0, 17, 128, 1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49,
},
chunk: chunks.InfoTrackerListChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{1, 0, 13, 128, 0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 17, Id: 2, HasSubchunks: true},
},
Data: chunks.InfoTrackerListChunkData{
Trackers: []chunks.InfoTrackerChunk{
{
Chunk: chunks.Chunk{
ChunkData: []byte{0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 13, Id: 1, HasSubchunks: true},
},
Data: chunks.InfoTrackerChunkData{
TrackerName: &chunks.InfoTrackerNameChunk{
Chunk: chunks.Chunk{
ChunkData: []byte{84, 114, 97, 99, 107, 101, 114, 32, 49},
Header: chunks.ChunkHeader{DataLen: 9, Id: 0, HasSubchunks: false},
},
Data: chunks.InfoTrackerNameChunkData{
TrackerName: "Tracker 1",
},
},
},
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
trackerChunks := [][]byte{}
for _, tracker := range testCase.chunk.Data.Trackers {
trackerChunks = append(trackerChunks, EncodeInfoTrackerChunk(tracker.Chunk.Header.Id, EncodeInfoTrackerNameChunk(tracker.Data.TrackerName.Data.TrackerName)))
}
actual := EncodeInfoTrackerListChunk(trackerChunks)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,36 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestInfoTrackerNameChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.InfoTrackerNameChunkData
}{
{
description: "InfoTrackerNameChunk",
expected: []byte{
0, 0, 9, 0, 84, 114, 97, 99, 107, 101, 114, 32, 49,
},
data: chunks.InfoTrackerNameChunkData{
TrackerName: "Tracker 1",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodeInfoTrackerNameChunk(testCase.data.TrackerName)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
@@ -0,0 +1,40 @@
package encoders
import (
"reflect"
"testing"
"github.com/jwetzell/psn-go/internal/chunks"
)
func TestPacketHeaderChunkEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
data chunks.PacketHeaderChunkData
}{
{
description: "PacketHeaderChunk",
expected: []byte{
0, 0, 12, 0, 210, 2, 150, 73, 0, 0, 0, 0, 2, 3, 1, 123,
},
data: chunks.PacketHeaderChunkData{
PacketTimestamp: 1234567890,
VersionHigh: 2,
VersionLow: 3,
FrameId: 1,
FramePacketCount: 123,
},
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
actual := EncodePacketHeaderChunk(testCase.data.PacketTimestamp, testCase.data.VersionHigh, testCase.data.VersionLow, testCase.data.FrameId, testCase.data.FramePacketCount)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("failed to encode chunk properly, expected: %v, actual: %v\n", testCase.expected, actual)
}
})
}
}
+7 -7
View File
@@ -1,7 +1,7 @@
package psn
import (
"github.com/jwetzell/psn-go/internal/decoders"
"github.com/jwetzell/psn-go/internal/chunks"
"github.com/jwetzell/psn-go/internal/encoders"
)
@@ -12,8 +12,6 @@ type XYZData struct {
}
type Tracker struct {
Id uint16
Name string
Pos *XYZData
Speed *XYZData
Ori *XYZData
@@ -21,6 +19,8 @@ type Tracker struct {
Accel *XYZData
TrgtPos *XYZData
Timestamp *uint64
Name string
Id uint16
}
func (t *Tracker) SetPos(x float32, y float32, z float32) {
@@ -138,12 +138,12 @@ func (t *Tracker) GetInfoChunk() []byte {
return encoders.EncodeInfoTrackerChunk(t.Id, encoders.EncodeInfoTrackerNameChunk(t.Name))
}
func (t *Tracker) UpdateInfo(infoTrackerChunk decoders.InfoTrackerChunk) {
func (t *Tracker) UpdateInfo(infoTrackerChunk chunks.InfoTrackerChunk) {
t.Id = infoTrackerChunk.Chunk.Header.Id
t.Name = infoTrackerChunk.Data.TrackerName.Data.TrackerName
}
func (t *Tracker) UpdateData(dataTrackerChunk decoders.DataTrackerChunk) {
func (t *Tracker) UpdateData(dataTrackerChunk chunks.DataTrackerChunk) {
if dataTrackerChunk.Data.Pos != nil {
t.SetPos(dataTrackerChunk.Data.Pos.Data.X, dataTrackerChunk.Data.Pos.Data.Y, dataTrackerChunk.Data.Pos.Data.Z)
}
@@ -173,13 +173,13 @@ func (t *Tracker) UpdateData(dataTrackerChunk decoders.DataTrackerChunk) {
}
}
func TrackerFromInfo(infoTrackerChunk decoders.InfoTrackerChunk) *Tracker {
func TrackerFromInfo(infoTrackerChunk chunks.InfoTrackerChunk) *Tracker {
var tracker Tracker
tracker.UpdateInfo(infoTrackerChunk)
return &tracker
}
func TrackerFromData(dataTrackerChunk decoders.DataTrackerChunk) *Tracker {
func TrackerFromData(dataTrackerChunk chunks.DataTrackerChunk) *Tracker {
var tracker Tracker
tracker.UpdateData(dataTrackerChunk)
return &tracker