diff options
-rw-r--r-- | LICENSE | 4 | ||||
-rw-r--r-- | README.markdown | 7 | ||||
-rw-r--r-- | nbt/nbt.go | 255 |
3 files changed, 266 insertions, 0 deletions
@@ -0,0 +1,4 @@ + DO WHATEVER THE FUCK YOU WANT, PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHATEVER THE FUCK YOU WANT. diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..15d4eb6 --- /dev/null +++ b/README.markdown @@ -0,0 +1,7 @@ +# gonbt + +A Go library for reading and writing [NBT](???) data. + +## Install + +To install gonbt, simply execute `go get github.com/kch42/gonbt/nbt` 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) +} |