From b28cc4f2da1ddd382cbe05e9ee5c5b134f4fca44 Mon Sep 17 00:00:00 2001 From: JunDao Date: Sun, 19 May 2019 00:15:14 +0800 Subject: [PATCH] Marshal without test and TagList --- nbt/example_test.go | 39 ++++++++++ nbt/marshal.go | 184 ++++++++++++++++++++++++++++++++++++++++++++ nbt/marshal_test.go | 35 +++++++++ nbt/nbt.go | 2 + 4 files changed, 260 insertions(+) create mode 100644 nbt/example_test.go create mode 100644 nbt/marshal.go create mode 100644 nbt/marshal_test.go diff --git a/nbt/example_test.go b/nbt/example_test.go new file mode 100644 index 0000000..406954f --- /dev/null +++ b/nbt/example_test.go @@ -0,0 +1,39 @@ +package nbt + +import ( + "bytes" + "fmt" +) + +func ExampleUnmarshal() { + var data = []byte{ + 0x08, 0x00, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x09, + 0x42, 0x61, 0x6e, 0x61, 0x6e, 0x72, 0x61, 0x6d, 0x61, + } + + var Name string + + if err := Unmarshal(data, &Name); err != nil { + panic(err) + } + + fmt.Println(Name) + + // Output: Bananrama +} + +func ExampleMarshal() { + var value = struct { + Name string `nbt:"name"` + }{"Tnze"} + + var buf bytes.Buffer + if err := Marshal(&buf, value); err != nil { + panic(err) + } + + fmt.Printf("% 02x ", buf.Bytes()) + + // Output: + // 0a 00 00 08 00 00 00 04 54 6e 7a 65 00 +} diff --git a/nbt/marshal.go b/nbt/marshal.go new file mode 100644 index 0000000..f13f8bf --- /dev/null +++ b/nbt/marshal.go @@ -0,0 +1,184 @@ +package nbt + +import ( + "errors" + "io" + "math" + "reflect" +) + +func Marshal(w io.Writer, v interface{}) error { + return NewEncoder(w).Encode(v) +} + +type Encoder struct { + w io.Writer +} + +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{w: w} +} + +func (e *Encoder) Encode(v interface{}) error { + val := reflect.ValueOf(v) + return e.marshal(val, "") +} + +func (e *Encoder) marshal(val reflect.Value, tagName string) error { + switch vk := val.Kind(); vk { + default: + return errors.New("unknown type " + vk.String()) + + case reflect.Uint8: + if err := e.writeTag(TagByte, tagName); err != nil { + return err + } + _, err := e.w.Write([]byte{byte(val.Uint())}) + return err + + case reflect.Int16, reflect.Uint16: + if err := e.writeTag(TagShort, tagName); err != nil { + return err + } + return e.writeInt16(int16(val.Int())) + + case reflect.Int32, reflect.Uint32: + if err := e.writeTag(TagInt, tagName); err != nil { + return err + } + return e.writeInt32(int32(val.Int())) + + case reflect.Float32: + if err := e.writeTag(TagFloat, tagName); err != nil { + return err + } + return e.writeInt32(int32(math.Float32bits(float32(val.Float())))) + + case reflect.Int64, reflect.Uint64: + if err := e.writeTag(TagLong, tagName); err != nil { + return err + } + return e.writeInt64(int64(val.Int())) + + case reflect.Float64: + if err := e.writeTag(TagDouble, tagName); err != nil { + return err + } + return e.writeInt64(int64(math.Float64bits(val.Float()))) + + case reflect.Array, reflect.Slice: + switch val.Type().Elem().Kind() { + case reflect.Uint8: // []byte + if err := e.writeTag(TagByteArray, tagName); err != nil { + return err + } + if err := e.writeInt32(int32(val.Len())); err != nil { + return err + } + _, err := e.w.Write(val.Bytes()) + return err + + case reflect.Int32: + if err := e.writeTag(TagIntArray, tagName); err != nil { + return err + } + n := val.Len() + if err := e.writeInt32(int32(n)); err != nil { + return err + } + for i := 0; i < n; i++ { + if err := e.writeInt32(int32(val.Index(i).Int())); err != nil { + return err + } + } + + case reflect.Int64: + if err := e.writeTag(TagLongArray, tagName); err != nil { + return err + } + n := val.Len() + if err := e.writeInt32(int32(n)); err != nil { + return err + } + for i := 0; i < n; i++ { + if err := e.writeInt64(val.Index(i).Int()); err != nil { + return err + } + } + + default: + return errors.New("unknow type " + val.Type().String() + " slice") + } + + case reflect.String: + if err := e.writeTag(TagString, tagName); err != nil { + return err + } + if err := e.writeInt16(int16(val.Len())); err != nil { + return err + } + _, err := e.w.Write([]byte(val.String())) + return err + + case reflect.Struct: + if err := e.writeTag(TagCompound, ""); err != nil { + return err + } + + n := val.NumField() + for i := 0; i < n; i++ { + f := val.Type().Field(i) + tag := f.Tag.Get("nbt") + if (f.PkgPath != "" && !f.Anonymous) || tag == "-" { + continue // Private field + } + + tagName := f.Name + if tag != "" { + tagName = tag + } + + err := e.marshal(val.Field(i), tagName) + if err != nil { + return err + } + } + _, err := e.w.Write([]byte{TagEnd}) + return err + } + return nil +} + +func (e *Encoder) writeTag(tagType byte, tagName string) error { + if _, err := e.w.Write([]byte{tagType}); err != nil { + return err + } + bName := []byte(tagName) + if err := e.writeInt16(int16(len(bName))); err != nil { + return err + } + _, err := e.w.Write(bName) + return err +} + +func (e *Encoder) writeNamelessTag(tagType byte, tagName string) error { + _, err := e.w.Write([]byte{tagType}) + return err +} + +func (e *Encoder) writeInt16(n int16) error { + e.w.Write([]byte{byte(n >> 8), byte(n)}) + return nil +} + +func (e *Encoder) writeInt32(n int32) error { + e.w.Write([]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)}) + return nil +} + +func (e *Encoder) writeInt64(n int64) error { + e.w.Write([]byte{ + byte(n >> 56), byte(n >> 48), byte(n >> 40), byte(n >> 32), + byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)}) + return nil +} diff --git a/nbt/marshal_test.go b/nbt/marshal_test.go new file mode 100644 index 0000000..0bc0bd8 --- /dev/null +++ b/nbt/marshal_test.go @@ -0,0 +1,35 @@ +package nbt + +import ( + "bytes" + "reflect" + "testing" +) + +func TestMarshal(t *testing.T) { + var ( + want = []byte{ + 0x0A, 0, 0, + 0x08, 0, 4, 0x4e, 0x61, 0x6d, 0x65, 0, 4, 0x54, 0x6e, 0x7a, 0x65, + 0x01, 0x00, 0x08, 0x42, 0x79, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0xFF, + + 0, + } + value struct { + Name string + ByteTest byte + } + ) + value.Name = "Tnze" + value.ByteTest = 0xFF + + var buf bytes.Buffer + if err := Marshal(&buf, value); err != nil { + t.Fatal(err) + } + + gets := buf.Bytes() + if !reflect.DeepEqual(gets, want) { + t.Errorf("marshal wrong: get [% 02x], want [% 02x]", gets, want) + } +} diff --git a/nbt/nbt.go b/nbt/nbt.go index 6f2c643..bf21959 100644 --- a/nbt/nbt.go +++ b/nbt/nbt.go @@ -1,3 +1,5 @@ +// Package nbt implement the Named Binary Tag format of Minecraft. +// It provides api like encoding/xml package. package nbt import (