From 37d4179bb261d0fe2914ae20187cb25c9bc16b7b Mon Sep 17 00:00:00 2001 From: Tnze Date: Mon, 24 Apr 2023 21:59:30 +0800 Subject: [PATCH] Finish development of fastnbt --- nbt/fastnbt/decode.go | 18 +++---- nbt/fastnbt/encode.go | 4 ++ nbt/fastnbt/encode_test.go | 74 +++++++++++++++++++++++++++ nbt/fastnbt/types.go | 102 ++++++++++++++++++++++++++++++------- 4 files changed, 169 insertions(+), 29 deletions(-) diff --git a/nbt/fastnbt/decode.go b/nbt/fastnbt/decode.go index bd41612..d77ca58 100644 --- a/nbt/fastnbt/decode.go +++ b/nbt/fastnbt/decode.go @@ -1,6 +1,7 @@ package fastnbt import ( + "encoding/binary" "errors" "fmt" "io" @@ -8,10 +9,6 @@ import ( "github.com/Tnze/go-mc/nbt" ) -//func (v *Value) Parse(data []byte) { -// // TODO -//} - func (v *Value) UnmarshalNBT(tagType byte, r nbt.DecoderReader) error { v.tag = tagType var buf [8]byte @@ -49,7 +46,7 @@ func (v *Value) UnmarshalNBT(tagType byte, r nbt.DecoderReader) error { } v.data = append(v.data[:0], make([]byte, 4+n)...) - v.data[0], v.data[1], v.data[2], v.data[3] = byte(n>>24), byte(n>>16), byte(n>>8), byte(n) + binary.BigEndian.PutUint32(v.data, uint32(n)) _, err = io.ReadFull(r, v.data[4:]) if err != nil { @@ -63,7 +60,7 @@ func (v *Value) UnmarshalNBT(tagType byte, r nbt.DecoderReader) error { } v.data = append(v.data[:0], make([]byte, 2+n)...) - v.data[0], v.data[1] = byte(n>>8), byte(n) + binary.BigEndian.PutUint16(v.data, uint16(n)) _, err = io.ReadFull(r, v.data[2:]) if err != nil { @@ -119,7 +116,7 @@ func (v *Value) UnmarshalNBT(tagType byte, r nbt.DecoderReader) error { } v.data = append(v.data[:0], make([]byte, 4+n*4)...) - v.data[0], v.data[1], v.data[2], v.data[3] = byte(n>>24), byte(n>>16), byte(n>>8), byte(n) + binary.BigEndian.PutUint32(v.data, uint32(n)) _, err = io.ReadFull(r, v.data[4:]) if err != nil { @@ -133,7 +130,7 @@ func (v *Value) UnmarshalNBT(tagType byte, r nbt.DecoderReader) error { } v.data = append(v.data[:0], make([]byte, 4+n*8)...) - v.data[0], v.data[1], v.data[2], v.data[3] = byte(n>>24), byte(n>>16), byte(n>>8), byte(n) + binary.BigEndian.PutUint32(v.data, uint32(n)) _, err = io.ReadFull(r, v.data[4:]) if err != nil { @@ -161,14 +158,13 @@ func readTag(r nbt.DecoderReader) (tagType byte, tagName string, err error) { func readInt16(r nbt.DecoderReader) (int16, error) { var data [2]byte _, err := io.ReadFull(r, data[:]) - return int16(data[0])<<8 | int16(data[1]), err + return int16(binary.BigEndian.Uint16(data[:])), err } func readInt32(r nbt.DecoderReader) (int32, error) { var data [4]byte _, err := io.ReadFull(r, data[:]) - return int32(data[0])<<24 | int32(data[1])<<16 | - int32(data[2])<<8 | int32(data[3]), err + return int32(binary.BigEndian.Uint32(data[:])), err } func readString(r nbt.DecoderReader) (string, error) { diff --git a/nbt/fastnbt/encode.go b/nbt/fastnbt/encode.go index f1fc998..1e23b58 100644 --- a/nbt/fastnbt/encode.go +++ b/nbt/fastnbt/encode.go @@ -12,6 +12,10 @@ func (v *Value) TagType() byte { return v.tag } func (v *Value) MarshalNBT(w io.Writer) (err error) { switch v.tag { case nbt.TagEnd: + _, err = w.Write([]byte{0}) + if err != nil { + return err + } case nbt.TagByte, nbt.TagShort, nbt.TagInt, nbt.TagLong, nbt.TagFloat, nbt.TagDouble, nbt.TagByteArray, nbt.TagString, nbt.TagIntArray, nbt.TagLongArray: diff --git a/nbt/fastnbt/encode_test.go b/nbt/fastnbt/encode_test.go index aec94aa..38b8663 100644 --- a/nbt/fastnbt/encode_test.go +++ b/nbt/fastnbt/encode_test.go @@ -1 +1,75 @@ package fastnbt + +import ( + "bytes" + "testing" + + "github.com/Tnze/go-mc/nbt" +) + +func TestValue_new(t *testing.T) { + if val := NewBoolean(true); val.Boolean() != true { + t.Error("encode bool error") + } + if val := NewBoolean(false); val.Boolean() != false { + t.Error("encode bool error") + } + if val := NewByte(127); val.Byte() != 127 { + t.Error("encode byte error") + } + if val := NewShort(32767); val.Short() != 32767 { + t.Error("encode short error") + } + if val := NewInt(2147483647); val.Int() != 2147483647 { + t.Error("encode int error") + } + if val := NewLong(9223372036854775807); val.Long() != 9223372036854775807 { + t.Error("encode long error") + } + if val := NewString("HELLO WORLD THIS IS A TEST STRING ÅÄÖ!"); val.String() != "HELLO WORLD THIS IS A TEST STRING ÅÄÖ!" { + t.Error("encode string error") + } + if val := NewFloat(0.49823147); val.Float() != 0.49823147 { + t.Error("encode float error") + } + if val := NewDouble(0.4931287132182315); val.Double() != 0.4931287132182315 { + t.Error("encode double error") + } + + byteArray := make([]byte, 1000) + for n := 0; n < 1000; n++ { + byteArray[n] = byte((n*n*255 + n*7) % 100) + } + if val := NewByteArray(byteArray); !bytes.Equal(byteArray, val.ByteArray()) { + t.Error("encode byteArray error") + } + + val := NewCompound() + val.Set("a", NewString("tnze")) + if val.Get("a").String() != "tnze" || val.Compound().Len() != 1 { + t.Error("encode compound error") + } +} + +func TestValue_bigTest(t *testing.T) { + data, err := nbt.Marshal(nbt.StringifiedMessage(bigTestSNBT)) + if err != nil { + t.Fatal(err) + } + + var val Value + err = nbt.Unmarshal(data, &val) + if err != nil { + t.Fatal(err) + } + + var data2 []byte + data2, err = nbt.Marshal(&val) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(data, data2) { + t.Fail() + } +} diff --git a/nbt/fastnbt/types.go b/nbt/fastnbt/types.go index 01e9f96..ce8a6b3 100644 --- a/nbt/fastnbt/types.go +++ b/nbt/fastnbt/types.go @@ -1,6 +1,7 @@ package fastnbt import ( + "encoding/binary" "math" "github.com/Tnze/go-mc/nbt" @@ -13,7 +14,69 @@ type Value struct { tag byte // nbt.Tag* } -func (v *Value) Bool() bool { +func NewBoolean(v bool) *Value { + data := byte(0) + if v { + data = 1 + } + return &Value{tag: nbt.TagByte, data: []byte{data}} +} + +func NewByte(v int8) *Value { + return &Value{tag: nbt.TagByte, data: []byte{byte(v)}} +} + +func NewShort(v int16) *Value { + data := make([]byte, 2) + binary.BigEndian.PutUint16(data, uint16(v)) + return &Value{tag: nbt.TagShort, data: data} +} + +func NewInt(v int32) *Value { + data := make([]byte, 4) + binary.BigEndian.PutUint32(data, uint32(v)) + return &Value{tag: nbt.TagInt, data: data} +} + +func NewLong(v int64) *Value { + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, uint64(v)) + return &Value{tag: nbt.TagLong, data: data} +} + +func NewFloat(f float32) *Value { + data := make([]byte, 4) + binary.BigEndian.PutUint32(data, math.Float32bits(f)) + return &Value{tag: nbt.TagFloat, data: data} +} + +func NewDouble(d float64) *Value { + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, math.Float64bits(d)) + return &Value{tag: nbt.TagDouble, data: data} +} + +func NewByteArray(v []byte) *Value { + data := make([]byte, 4, 4+len(v)) + binary.BigEndian.PutUint32(data, uint32(len(v))) + return &Value{tag: nbt.TagByteArray, data: append(data, v...)} +} + +func NewString(str string) *Value { + data := make([]byte, 2, 2+len(str)) + binary.BigEndian.PutUint16(data, uint16(len(str))) + return &Value{tag: nbt.TagString, data: append(data, str...)} +} + +func NewList(elems ...*Value) *Value { + return &Value{tag: nbt.TagList, list: elems} +} + +func NewCompound() *Value { + return &Value{tag: nbt.TagCompound} +} + +func (v *Value) Boolean() bool { if v.tag != nbt.TagByte { return false } @@ -31,51 +94,45 @@ func (v *Value) Short() int16 { if v.tag != nbt.TagShort { return 0 } - return int16(v.data[0])<<8 | int16(v.data[1]) + return int16(binary.BigEndian.Uint16(v.data)) } func (v *Value) Int() int32 { if v.tag != nbt.TagInt { return 0 } - return int32(v.data[0])<<24 | int32(v.data[1])<<16 | - int32(v.data[2])<<8 | int32(v.data[3]) + return int32(binary.BigEndian.Uint32(v.data)) } func (v *Value) Long() int64 { if v.tag != nbt.TagLong { return 0 } - return int64(v.data[0])<<56 | int64(v.data[1])<<48 | - int64(v.data[2])<<40 | int64(v.data[3])<<32 | - int64(v.data[4])<<24 | int64(v.data[5])<<16 | - int64(v.data[6])<<8 | int64(v.data[7]) + return int64(binary.BigEndian.Uint64(v.data)) } func (v *Value) Float() float32 { if v.tag != nbt.TagFloat { - return 0 + return float32(math.NaN()) } - return math.Float32frombits( - uint32(v.data[0])<<24 | uint32(v.data[1])<<16 | - uint32(v.data[2])<<8 | uint32(v.data[3])) + return math.Float32frombits(binary.BigEndian.Uint32(v.data)) } func (v *Value) Double() float64 { if v.tag != nbt.TagDouble { - return 0 + return math.NaN() } - return math.Float64frombits( - uint64(v.data[0])<<56 | uint64(v.data[1])<<48 | - uint64(v.data[2])<<40 | uint64(v.data[3])<<32 | - uint64(v.data[4])<<24 | uint64(v.data[5])<<16 | - uint64(v.data[6])<<8 | uint64(v.data[7])) + return math.Float64frombits(binary.BigEndian.Uint64(v.data)) } func (v *Value) List() []*Value { return v.list } +func (v *Value) Compound() *Compound { + return &v.comp +} + func (v *Value) ByteArray() []byte { if v.tag != nbt.TagByteArray { return nil @@ -83,6 +140,15 @@ func (v *Value) ByteArray() []byte { return v.data[4:] } +func (v *Value) IntArray() []int32 { + length := binary.BigEndian.Uint32(v.data) + ret := make([]int32, length) + for i := uint32(0); i < length; i += 4 { + ret[i] = int32(binary.BigEndian.Uint32(v.data[i:])) + } + return ret +} + func (v *Value) String() string { if v.tag != nbt.TagString { return ""