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.DataInput; 024import java.io.IOException; 025 026/** 027 * @author The TridentSDK Team 028 * @since 0.3-alpha-DP 029 */ 030public class NBTDecoder { 031 final DataInput input; 032 033 public NBTDecoder(DataInput input) { 034 this.input = input; 035 } 036 037 public CompoundTag decode() throws NBTException { 038 try { 039 return this.decode(this.input.readByte()); 040 } catch (IOException e) { 041 TridentLogger.get().error(new NBTException("IO Error decoding the NBT Data", e)); 042 return null; 043 } 044 } 045 046 public CompoundTag decode(byte b) throws NBTException { 047 TagType initType = TagType.fromId(b); 048 049 //NBT source must start with a compound tag or is invalid 050 if (initType != TagType.COMPOUND) { 051 TridentLogger.get().error(new NBTException("NBT Data must start with a Compound Tag.")); 052 } 053 054 //Create the resulting CompoundTag to return 055 //Uses recursion to recursively walk through the tag tree 056 try { 057 return this.resolveCompoundTag(this.readString()); 058 } catch (IOException e) { 059 TridentLogger.get().error(new NBTException("IO Error decoding the NBT Data", e)); 060 return null; 061 } 062 } 063 064 private CompoundTag resolveCompoundTag(String name) throws IOException { 065 CompoundTag compound = new CompoundTag(name); 066 TagType innerType; 067 068 while ((innerType = TagType.fromId(this.input.readByte())) != TagType.END) { 069 compound.addTag(this.resolveTag(innerType, true)); 070 } 071 072 return compound; 073 } 074 075 private ListTag resolveListTag(String name) throws IOException { 076 TagType listType = TagType.fromId(this.input.readByte()); 077 ListTag list = new ListTag(name, listType); 078 int length = this.input.readInt(); 079 080 for (int i = 0; i < length; i++) { 081 list.addTag(this.resolveTag(listType, false)); 082 } 083 084 return list; 085 } 086 087 private NBTTag resolveTag(TagType type, boolean withName) throws IOException { 088 //Reads name if required 089 String name = null; 090 if (withName) { 091 name = this.readString(); 092 } 093 094 NBTTag result; 095 switch (type) { 096 case BYTE: 097 result = new ByteTag(name); 098 result.asType(ByteTag.class).setValue(this.input.readByte()); 099 break; 100 101 case SHORT: 102 result = new ShortTag(name); 103 result.asType(ShortTag.class).setValue(this.input.readShort()); 104 break; 105 106 case INT: 107 result = new IntTag(name); 108 result.asType(IntTag.class).setValue(this.input.readInt()); 109 break; 110 111 case LONG: 112 result = new LongTag(name); 113 result.asType(LongTag.class).setValue(this.input.readLong()); 114 break; 115 116 case FLOAT: 117 result = new FloatTag(name); 118 result.asType(FloatTag.class).setValue(this.input.readFloat()); 119 break; 120 121 case DOUBLE: 122 result = new DoubleTag(name); 123 result.asType(DoubleTag.class).setValue(this.input.readDouble()); 124 break; 125 126 case BYTE_ARRAY: 127 result = new ByteArrayTag(name); 128 int balength = this.input.readInt(); 129 byte[] babytes = new byte[balength]; 130 131 this.input.readFully(babytes); 132 result.asType(ByteArrayTag.class).setValue(babytes); 133 134 break; 135 136 case STRING: 137 result = new StringTag(name); 138 result.asType(StringTag.class).setValue(this.readString()); 139 140 break; 141 142 case LIST: 143 result = this.resolveListTag(name); 144 break; 145 146 case COMPOUND: 147 result = this.resolveCompoundTag(name); 148 break; 149 150 case INT_ARRAY: 151 result = new IntArrayTag(name); 152 int ialength = this.input.readInt(); 153 int[] array = new int[ialength]; 154 155 for (int i = 0; i < array.length; i++) { 156 array[i] = this.input.readInt(); 157 } 158 159 result.asType(IntArrayTag.class).setValue(array); 160 break; 161 162 default: 163 result = new NullTag(name); 164 break; 165 } 166 167 return result; 168 } 169 170 private String readString() throws IOException { 171 short length = this.input.readShort(); 172 byte[] bytes = new byte[(int) length]; 173 174 this.input.readFully(bytes); 175 176 return new String(bytes, Charsets.UTF_8); 177 } 178}