001/*
002 * Trident - A Multithreaded Server Alternative
003 * Copyright 2014 The TridentSDK Team
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package net.tridentsdk.meta.nbt;
019
020import com.google.common.base.Charsets;
021import net.tridentsdk.util.TridentLogger;
022
023import java.io.DataOutput;
024import java.io.IOException;
025import java.util.List;
026
027/**
028 * @author The TridentSDK Team
029 * @since 0.3-alpha-DP
030 */
031public class NBTEncoder {
032    final DataOutput output;
033
034    public NBTEncoder(DataOutput output) {
035        this.output = output;
036    }
037
038    public void encode(CompoundTag tag) throws NBTException {
039        try {
040            this.writeTag(tag);
041        } catch (IOException e) {
042            TridentLogger.get().error(new NBTException("IO Error encoding the NBT Data", e));
043        }
044    }
045
046    private void writeCompoundTag(CompoundTag tag) throws IOException {
047        for (NBTTag inner : tag.listTags()) {
048            this.writeTag(inner);
049        }
050        //Write Tag_End to signify end of Compound
051        this.output.writeByte(TagType.END.id());
052    }
053
054    private void writeListTag(ListTag tag) throws IOException {
055        //Write inner tag-type id
056        this.output.writeByte(tag.getInnerType().id());
057
058        List<NBTTag> innerTags = tag.listTags();
059        this.output.writeInt(innerTags.size());
060
061        for (NBTTag inner : innerTags) {
062            this.writeTag(inner, false, false);
063        }
064    }
065
066    public void writeTag(NBTTag tag) throws IOException {
067        writeTag(tag, true, true);
068    }
069
070    private void writeTag(NBTTag tag, boolean name, boolean id) throws IOException {
071        if (id) {
072            this.output.writeByte(tag.type().id());
073        }
074
075        if (tag.hasName() && name) {
076            this.writeString(tag.name());
077        }
078
079        switch (tag.type()) {
080            case BYTE:
081                this.output.writeByte((int) tag.asType(ByteTag.class).value());
082                break;
083
084            case SHORT:
085                this.output.writeShort((int) tag.asType(ShortTag.class).value());
086                break;
087
088            case INT:
089                this.output.writeInt(tag.asType(IntTag.class).value());
090                break;
091
092            case LONG:
093                this.output.writeLong(tag.asType(LongTag.class).value());
094                break;
095
096            case FLOAT:
097                this.output.writeFloat(tag.asType(FloatTag.class).value());
098                break;
099            case DOUBLE:
100                this.output.writeDouble(tag.asType(DoubleTag.class).value());
101                break;
102
103            case BYTE_ARRAY:
104                byte[] barray = tag.asType(ByteArrayTag.class).value();
105                this.output.writeInt(barray.length);
106                this.output.write(barray);
107                break;
108
109            case STRING:
110                this.writeString(tag.asType(StringTag.class).value());
111                break;
112
113            case LIST:
114                this.writeListTag(tag.asType(ListTag.class));
115                break;
116
117            case COMPOUND:
118                this.writeCompoundTag(tag.asType(CompoundTag.class));
119                break;
120
121            case INT_ARRAY:
122                int[] iarray = tag.asType(IntArrayTag.class).value();
123                this.output.writeInt(iarray.length);
124                for (int anIarray : iarray) {
125                    this.output.writeInt(anIarray);
126                }
127                break;
128
129            default:
130                //Shouldn't/Can't happen
131                break;
132        }
133    }
134
135    private void writeString(String s) throws IOException {
136        this.output.writeShort(s.length());
137        this.output.write(s.getBytes(Charsets.UTF_8));
138    }
139}