summaryrefslogtreecommitdiff
path: root/nbt
diff options
context:
space:
mode:
Diffstat (limited to 'nbt')
-rw-r--r--nbt/nbt.go255
1 files changed, 255 insertions, 0 deletions
diff --git a/nbt/nbt.go b/nbt/nbt.go
new file mode 100644
index 0000000..6b1a814
--- /dev/null
+++ b/nbt/nbt.go
@@ -0,0 +1,255 @@
+package nbt
+
+import (
+ "encoding/binary"
+ "errors"
+ "io"
+)
+
+const (
+ TAG_End = iota
+ TAG_Byte
+ TAG_Short
+ TAG_Int
+ TAG_Long
+ TAG_Float
+ TAG_Double
+ TAG_Byte_Array
+ TAG_String
+ TAG_List
+ TAG_Compound
+ TAG_Int_Array
+)
+
+type Tag struct {
+ Type byte
+ Payload interface{}
+}
+
+type TagList struct {
+ Type byte
+ Elems []interface{}
+}
+
+type TagCompound map[string]Tag
+
+func readByte(r io.Reader) (byte, error) {
+ buf := make([]byte, 1)
+ _, err := r.Read(buf)
+ return buf[0], err
+}
+
+func readTagData(r io.Reader, tt byte) (interface{}, error) {
+ switch tt {
+ case TAG_End:
+ case TAG_Byte:
+ var v uint8
+ err := binary.Read(r, binary.BigEndian, &v)
+ return v, err
+ case TAG_Short:
+ var v int16
+ err := binary.Read(r, binary.BigEndian, &v)
+ return v, err
+ case TAG_Int:
+ var v int32
+ err := binary.Read(r, binary.BigEndian, &v)
+ return v, err
+ case TAG_Long:
+ var v int64
+ err := binary.Read(r, binary.BigEndian, &v)
+ return v, err
+ case TAG_Float:
+ var v float32
+ err := binary.Read(r, binary.BigEndian, &v)
+ return v, err
+ case TAG_Double:
+ var v float64
+ err := binary.Read(r, binary.BigEndian, &v)
+ return v, err
+ case TAG_Byte_Array:
+ var l int32
+ if err := binary.Read(r, binary.BigEndian, &l); err != nil {
+ return nil, err
+ }
+ if l < 0 {
+ return nil, errors.New("Byte array has negative length?")
+ }
+
+ data := make([]byte, l)
+ _, err := io.ReadFull(r, data)
+ return data, err
+ case TAG_String:
+ var l int16
+ if err := binary.Read(r, binary.BigEndian, &l); err != nil {
+ return nil, err
+ }
+ if l < 0 {
+ return nil, errors.New("String has negative length?")
+ }
+
+ data := make([]byte, l)
+ _, err := io.ReadFull(r, data)
+ return string(data), err
+ case TAG_List:
+ ltt, err := readByte(r)
+ if err != nil {
+ return nil, err
+ }
+
+ var l int32
+ if err := binary.Read(r, binary.BigEndian, &l); err != nil {
+ return nil, err
+ }
+ if l < 0 {
+ return nil, errors.New("List has negative length?")
+ }
+
+ tl := TagList{Type: ltt, Elems: make([]interface{}, l)}
+ for i := 0; i < int(l); i++ {
+ if tl.Elems[i], err = readTagData(r, ltt); err != nil {
+ return nil, err
+ }
+ }
+ return tl, nil
+ case TAG_Compound:
+ comp := make(TagCompound)
+ for {
+ tag, name, err := ReadNamedTag(r)
+ if err != nil {
+ return nil, err
+ }
+ if tag.Type == TAG_End {
+ break
+ }
+ comp[name] = tag
+ }
+ return comp, nil
+ case TAG_Int_Array:
+ var l int32
+ if err := binary.Read(r, binary.BigEndian, &l); err != nil {
+ return nil, err
+ }
+ if l < 0 {
+ return nil, errors.New("Int Array has negative length?")
+ }
+
+ data := make([]int32, l)
+ for i := 0; i < int(l); i++ {
+ var e int32
+ if err := binary.Read(r, binary.BigEndian, &e); err != nil {
+ return nil, err
+ }
+ data[i] = e
+ }
+ return data, nil
+ }
+
+ return nil, errors.New("Unknown tag type")
+}
+
+func ReadNamedTag(r io.Reader) (Tag, string, error) {
+ tt, err := readByte(r)
+ if err != nil {
+ return Tag{}, "", err
+ }
+ if tt == TAG_End {
+ return Tag{Type: tt}, "", nil
+ }
+
+ name, err := readTagData(r, TAG_String)
+ if err != nil {
+ return Tag{}, "", err
+ }
+
+ td, err := readTagData(r, tt)
+ return Tag{Type: tt, Payload: td}, name.(string), err
+}
+
+func writeByte(w io.Writer, b byte) error {
+ _, err := w.Write([]byte{b})
+ return err
+}
+
+func writeTagData(w io.Writer, tt byte, data interface{}) error {
+ switch tt {
+ case TAG_End:
+ return nil
+ case TAG_Byte:
+ return writeByte(w, data.(byte))
+ case TAG_Short:
+ return binary.Write(w, binary.BigEndian, data.(int16))
+ case TAG_Int:
+ return binary.Write(w, binary.BigEndian, data.(int32))
+ case TAG_Long:
+ return binary.Write(w, binary.BigEndian, data.(int64))
+ case TAG_Float:
+ return binary.Write(w, binary.BigEndian, data.(float32))
+ case TAG_Double:
+ return binary.Write(w, binary.BigEndian, data.(float64))
+ case TAG_Byte_Array:
+ slice := data.([]byte)
+ if err := binary.Write(w, binary.BigEndian, int32(len(slice))); err != nil {
+ return err
+ }
+ _, err := w.Write(slice)
+ return err
+ case TAG_String:
+ strEnc := []byte(data.(string))
+ if err := binary.Write(w, binary.BigEndian, int16(len(strEnc))); err != nil {
+ return err
+ }
+ _, err := w.Write(strEnc)
+ return err
+ case TAG_List:
+ list := data.(TagList)
+ if err := writeByte(w, list.Type); err != nil {
+ return err
+ }
+
+ if err := binary.Write(w, binary.BigEndian, int32(len(list.Elems))); err != nil {
+ return err
+ }
+
+ for _, el := range list.Elems {
+ if err := writeTagData(w, list.Type, el); err != nil {
+ return err
+ }
+ }
+ return nil
+ case TAG_Compound:
+ comp := data.(TagCompound)
+ for name, tag := range comp {
+ if err := WriteNamedTag(w, name, tag); err != nil {
+ return err
+ }
+ }
+ return writeByte(w, TAG_End)
+ case TAG_Int_Array:
+ slice := data.([]int32)
+ if err := binary.Write(w, binary.BigEndian, int32(len(slice))); err != nil {
+ return err
+ }
+
+ for _, el := range slice {
+ if err := binary.Write(w, binary.BigEndian, el); err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }
+
+ return errors.New("Unknown tage type")
+}
+
+func WriteNamedTag(w io.Writer, name string, tag Tag) error {
+ if err := writeByte(w, tag.Type); err != nil {
+ return err
+ }
+
+ if err := writeTagData(w, TAG_String, name); err != nil {
+ return err
+ }
+
+ return writeTagData(w, tag.Type, tag.Payload)
+}