Add stateful unpacker
This commit is contained in:
parent
309062c7d3
commit
dcf26f818f
116
packer/packer.go
116
packer/packer.go
|
@ -1,9 +1,113 @@
|
||||||
package packer
|
package packer
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
func PackInt(num int) ([]byte, error) {
|
type Unpacker struct {
|
||||||
dstLen := 4
|
data []byte
|
||||||
|
idx int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Unpacker) Reset(data []byte) {
|
||||||
|
u.data = slices.Clone(data)
|
||||||
|
u.idx = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// first byte of the current buffer
|
||||||
|
func (u *Unpacker) byte() byte {
|
||||||
|
return u.data[u.idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume one byte
|
||||||
|
func (u *Unpacker) getByte() byte {
|
||||||
|
b := u.data[u.idx]
|
||||||
|
u.idx++
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Sanitize = 1
|
||||||
|
SanitizeCC = 2
|
||||||
|
SanitizeSkipWhitespaces = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
func (u *Unpacker) GetStringSanitized(sanitizeType int) string {
|
||||||
|
bytes := []byte{}
|
||||||
|
|
||||||
|
skipping := sanitizeType&SanitizeSkipWhitespaces != 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
b := u.getByte()
|
||||||
|
if b == 0x00 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if skipping {
|
||||||
|
if b == ' ' || b == '\t' || b == '\n' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
skipping = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if sanitizeType&SanitizeCC != 0 {
|
||||||
|
if b < 32 {
|
||||||
|
b = ' '
|
||||||
|
}
|
||||||
|
} else if sanitizeType&Sanitize != 0 {
|
||||||
|
if b < 32 && !(b == '\r') && !(b == '\n') && !(b == '\t') {
|
||||||
|
b = ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = append(bytes, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Unpacker) GetString() string {
|
||||||
|
return u.GetStringSanitized(Sanitize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Unpacker) GetInt() int {
|
||||||
|
sign := int(u.byte()>>6) & 1
|
||||||
|
res := int(u.byte() & 0x3F)
|
||||||
|
// fake loop should only loop once
|
||||||
|
// its the poor mans goto hack
|
||||||
|
for {
|
||||||
|
if (u.byte() & 0x80) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
u.idx += 1
|
||||||
|
res |= int(u.byte()&0x7F) << 6
|
||||||
|
|
||||||
|
if (u.byte() & 0x80) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
u.idx += 1
|
||||||
|
res |= int(u.byte()&0x7F) << (6 + 7)
|
||||||
|
|
||||||
|
if (u.byte() & 0x80) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
u.idx += 1
|
||||||
|
res |= int(u.byte()&0x7F) << (6 + 7 + 7)
|
||||||
|
|
||||||
|
if (u.byte() & 0x80) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
u.idx += 1
|
||||||
|
res |= int(u.byte()&0x7F) << (6 + 7 + 7 + 7)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
u.idx += 1
|
||||||
|
res ^= -sign
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func PackInt(num int) []byte {
|
||||||
res := []byte{0x00}
|
res := []byte{0x00}
|
||||||
idx := 0
|
idx := 0
|
||||||
if num < 0 {
|
if num < 0 {
|
||||||
|
@ -14,10 +118,6 @@ func PackInt(num int) ([]byte, error) {
|
||||||
res[0] |= byte(num & 0x3F) // pack 6 bit into dst
|
res[0] |= byte(num & 0x3F) // pack 6 bit into dst
|
||||||
num >>= 6 // discard 6 bits
|
num >>= 6 // discard 6 bits
|
||||||
for num != 0 {
|
for num != 0 {
|
||||||
if idx > dstLen {
|
|
||||||
return nil, errors.New("Int too big")
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, 0x00)
|
res = append(res, 0x00)
|
||||||
|
|
||||||
res[idx] |= 0x80 // set extend bit
|
res[idx] |= 0x80 // set extend bit
|
||||||
|
@ -26,7 +126,7 @@ func PackInt(num int) ([]byte, error) {
|
||||||
num >>= 7 // discard 7 bits
|
num >>= 7 // discard 7 bits
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnpackInt(data []byte) int {
|
func UnpackInt(data []byte) int {
|
||||||
|
|
121
packer/packer_state_test.go
Normal file
121
packer/packer_state_test.go
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package packer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// unpack with state
|
||||||
|
|
||||||
|
func TestUnpackSimpleInts(t *testing.T) {
|
||||||
|
u := Unpacker{}
|
||||||
|
u.Reset([]byte{0x01, 0x02, 0x03, 0x0f})
|
||||||
|
|
||||||
|
got := u.GetInt()
|
||||||
|
want := 1
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = u.GetInt()
|
||||||
|
want = 2
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = u.GetInt()
|
||||||
|
want = 3
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = u.GetInt()
|
||||||
|
want = 15
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpackString(t *testing.T) {
|
||||||
|
u := Unpacker{}
|
||||||
|
u.Reset([]byte{'f', 'o', 'o', 0x00})
|
||||||
|
|
||||||
|
got := u.GetString()
|
||||||
|
want := "foo"
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpackTwoStrings(t *testing.T) {
|
||||||
|
u := Unpacker{}
|
||||||
|
u.Reset([]byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r', 0x00})
|
||||||
|
|
||||||
|
got := u.GetString()
|
||||||
|
want := "foo"
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = u.GetString()
|
||||||
|
want = "bar"
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpackMixed(t *testing.T) {
|
||||||
|
u := Unpacker{}
|
||||||
|
u.Reset([]byte{0x0F, 0x0F, 'f', 'o', 'o', 0x00, 'b', 'a', 'r', 0x00, 0x01})
|
||||||
|
|
||||||
|
// ints
|
||||||
|
{
|
||||||
|
got := u.GetInt()
|
||||||
|
want := 15
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = u.GetInt()
|
||||||
|
want = 15
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// strings
|
||||||
|
{
|
||||||
|
got := u.GetString()
|
||||||
|
want := "foo"
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = u.GetString()
|
||||||
|
want = "bar"
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ints
|
||||||
|
{
|
||||||
|
got := u.GetInt()
|
||||||
|
want := 1
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,107 +8,71 @@ import (
|
||||||
// pack
|
// pack
|
||||||
|
|
||||||
func TestPackSmallPositiveInts(t *testing.T) {
|
func TestPackSmallPositiveInts(t *testing.T) {
|
||||||
got, err := PackInt(1)
|
got := PackInt(1)
|
||||||
want := []byte{0x01}
|
want := []byte{0x01}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPackMultiBytePositiveInts(t *testing.T) {
|
func TestPackMultiBytePositiveInts(t *testing.T) {
|
||||||
got, err := PackInt(63)
|
got := PackInt(63)
|
||||||
want := []byte{0x3F}
|
want := []byte{0x3F}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err = PackInt(64)
|
got = PackInt(64)
|
||||||
want = []byte{0x80, 0x01}
|
want = []byte{0x80, 0x01}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err = PackInt(65)
|
got = PackInt(65)
|
||||||
want = []byte{0x81, 0x01}
|
want = []byte{0x81, 0x01}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPackSmallNegativeInts(t *testing.T) {
|
func TestPackSmallNegativeInts(t *testing.T) {
|
||||||
got, err := PackInt(-1)
|
got := PackInt(-1)
|
||||||
want := []byte{0x40}
|
want := []byte{0x40}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err = PackInt(-2)
|
got = PackInt(-2)
|
||||||
want = []byte{0x41}
|
want = []byte{0x41}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPackMultiByteNegativeInts(t *testing.T) {
|
func TestPackMultiByteNegativeInts(t *testing.T) {
|
||||||
got, err := PackInt(-63)
|
got := PackInt(-63)
|
||||||
want := []byte{0x7E}
|
want := []byte{0x7E}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err = PackInt(-64)
|
got = PackInt(-64)
|
||||||
want = []byte{0x7F}
|
want = []byte{0x7F}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err = PackInt(-65)
|
got = PackInt(-65)
|
||||||
want = []byte{0xC0, 0x01}
|
want = []byte{0xC0, 0x01}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
if !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("got %v, wanted %v", got, want)
|
t.Errorf("got %v, wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
15
teeworlds.go
15
teeworlds.go
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/teeworlds-go/huffman"
|
"github.com/teeworlds-go/huffman"
|
||||||
"github.com/teeworlds-go/teeworlds/chunk"
|
"github.com/teeworlds-go/teeworlds/chunk"
|
||||||
|
"github.com/teeworlds-go/teeworlds/packer"
|
||||||
"github.com/teeworlds-go/teeworlds/packet"
|
"github.com/teeworlds-go/teeworlds/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -168,7 +169,7 @@ func byteSliceToString(s []byte) string {
|
||||||
return string(s)
|
return string(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *TeeworldsClient) onSystemMsg(msg int, chunk chunk.Chunk) {
|
func (client *TeeworldsClient) onSystemMsg(msg int, chunk chunk.Chunk, u *packer.Unpacker) {
|
||||||
if msg == msgSysMapChange {
|
if msg == msgSysMapChange {
|
||||||
fmt.Println("got map change")
|
fmt.Println("got map change")
|
||||||
client.sendReady()
|
client.sendReady()
|
||||||
|
@ -180,7 +181,7 @@ func (client *TeeworldsClient) onSystemMsg(msg int, chunk chunk.Chunk) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *TeeworldsClient) onGameMsg(msg int, chunk chunk.Chunk) {
|
func (client *TeeworldsClient) onGameMsg(msg int, chunk chunk.Chunk, u *packer.Unpacker) {
|
||||||
if msg == msgGameReadyToEnter {
|
if msg == msgGameReadyToEnter {
|
||||||
fmt.Println("got ready to enter")
|
fmt.Println("got ready to enter")
|
||||||
client.sendEnterGame()
|
client.sendEnterGame()
|
||||||
|
@ -192,16 +193,18 @@ func (client *TeeworldsClient) onGameMsg(msg int, chunk chunk.Chunk) {
|
||||||
func (client *TeeworldsClient) onMessage(chunk chunk.Chunk) {
|
func (client *TeeworldsClient) onMessage(chunk chunk.Chunk) {
|
||||||
fmt.Printf("got chunk size=%d data=%v\n", chunk.Header.Size, chunk.Data)
|
fmt.Printf("got chunk size=%d data=%v\n", chunk.Header.Size, chunk.Data)
|
||||||
|
|
||||||
// TODO: we need to int unpack this because it can be 2 bytes long
|
u := packer.Unpacker{}
|
||||||
msg := int(chunk.Data[0])
|
u.Reset(chunk.Data)
|
||||||
|
|
||||||
|
msg := u.GetInt()
|
||||||
|
|
||||||
sys := msg&1 != 0
|
sys := msg&1 != 0
|
||||||
msg >>= 1
|
msg >>= 1
|
||||||
|
|
||||||
if sys {
|
if sys {
|
||||||
client.onSystemMsg(msg, chunk)
|
client.onSystemMsg(msg, chunk, &u)
|
||||||
} else {
|
} else {
|
||||||
client.onGameMsg(msg, chunk)
|
client.onGameMsg(msg, chunk, &u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue