package net.minecraft.nbt; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import org.apache.commons.collections4.trie.PatriciaTrie; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.util.ReportedException; public class NBTTagCompound extends NBTBase { private static final Logger logger = LogManager.getLogger(); public NBTTagCompound() { this.tagMap = new PatriciaTrie<>(); } public NBTTagCompound(final Map<String, NBTBase> data) { this.tagMap = data; } /** * The key-value pairs for the tag. Each key is a UTF string, each value is a tag. */ Map<String, NBTBase> tagMap; private static final String __OBFID = "CL_00001215"; /** * Write the actual data contents of the tag, implemented in NBT extension classes */ @Override void write(final DataOutput output) throws IOException { final Iterator<String> var2 = this.tagMap.keySet().iterator(); while (var2.hasNext()) { final String var3 = var2.next(); final NBTBase var4 = this.tagMap.get(var3); writeEntry(var3, var4, output); } output.writeByte(0); } @Override void read(final DataInput input, final int depth, final NBTSizeTracker sizeTracker) throws IOException { if (depth > 512) throw new RuntimeException("Tried to read NBT tag with too high complexity, depth > 512"); else { this.tagMap.clear(); byte var4; while ((var4 = readType(input, sizeTracker)) != 0) { final String var5 = readKey(input, sizeTracker); sizeTracker.read(16 * var5.length()); final NBTBase var6 = readNBT(var4, var5, input, depth + 1, sizeTracker); this.tagMap.put(var5, var6); } } } /** * Gets a set with the names of the keys in the tag compound. */ public Set<String> getKeySet() { return this.tagMap.keySet(); } /** * Gets the type byte for the tag. */ @Override public byte getId() { return (byte) 10; } /** * Stores the given tag into the map with the given string key. This is mostly used to store tag lists. */ public void setTag(final String key, final NBTBase value) { this.tagMap.put(key, value); } /** * Stores a new NBTTagByte with the given byte value into the map with the given string key. */ public void setByte(final String key, final byte value) { this.setTag(key, new NBTTagByte(value)); } /** * Stores a new NBTTagShort with the given short value into the map with the given string key. */ public void setShort(final String key, final short value) { this.setTag(key, new NBTTagShort(value)); } /** * Stores a new NBTTagInt with the given integer value into the map with the given string key. */ public void setInteger(final String key, final int value) { this.setTag(key, new NBTTagInt(value)); } /** * Stores a new NBTTagLong with the given long value into the map with the given string key. */ public void setLong(final String key, final long value) { this.setTag(key, new NBTTagLong(value)); } /** * Stores a new NBTTagFloat with the given float value into the map with the given string key. */ public void setFloat(final String key, final float value) { this.setTag(key, new NBTTagFloat(value)); } /** * Stores a new NBTTagDouble with the given double value into the map with the given string key. */ public void setDouble(final String key, final double value) { this.setTag(key, new NBTTagDouble(value)); } /** * Stores a new NBTTagString with the given string value into the map with the given string key. */ public void setString(final String key, final String value) { this.setTag(key, new NBTTagString(value)); } /** * Stores a new NBTTagByteArray with the given array as data into the map with the given string key. */ public void setByteArray(final String key, final byte[] value) { this.setTag(key, new NBTTagByteArray(value)); } /** * Stores a new NBTTagIntArray with the given array as data into the map with the given string key. */ public void setIntArray(final String key, final int[] value) { this.setTag(key, new NBTTagIntArray(value)); } /** * Stores the given boolean value as a NBTTagByte, storing 1 for true and 0 for false, using the given string key. */ public void setBoolean(final String key, final boolean value) { this.setByte(key, (byte) (value ? 1 : 0)); } /** * gets a generic tag with the specified name */ public NBTBase getTag(final String key) { return this.tagMap.get(key); } /** * Get the Type-ID for the entry with the given key */ public byte getTagType(final String key) { final NBTBase var2 = this.tagMap.get(key); return var2 != null ? var2.getId() : 0; } /** * Returns whether the given string has been previously stored as a key in the map. */ public boolean hasKey(final String key) { return this.tagMap.containsKey(key); } public boolean hasKey(final String key, final int type) { final byte var3 = this.getTagType(key); if (var3 == type) return true; else if (type != 99) { if (var3 > 0) ; return false; } else return var3 == 1 || var3 == 2 || var3 == 3 || var3 == 4 || var3 == 5 || var3 == 6; } /** * Retrieves a byte value using the specified key, or 0 if no such key was stored. */ public byte getByte(final String key) { try { return !this.hasKey(key, 99) ? 0 : ((NBTBase.NBTPrimitive) this.tagMap.get(key)).getByte(); } catch (final ClassCastException var3) { return (byte) 0; } } /** * Retrieves a short value using the specified key, or 0 if no such key was stored. */ public short getShort(final String key) { try { return !this.hasKey(key, 99) ? 0 : ((NBTBase.NBTPrimitive) this.tagMap.get(key)).getShort(); } catch (final ClassCastException var3) { return (short) 0; } } /** * Retrieves an integer value using the specified key, or 0 if no such key was stored. */ public int getInteger(final String key) { try { return !this.hasKey(key, 99) ? 0 : ((NBTBase.NBTPrimitive) this.tagMap.get(key)).getInt(); } catch (final ClassCastException var3) { return 0; } } /** * Retrieves a long value using the specified key, or 0 if no such key was stored. */ public long getLong(final String key) { try { return !this.hasKey(key, 99) ? 0L : ((NBTBase.NBTPrimitive) this.tagMap.get(key)).getLong(); } catch (final ClassCastException var3) { return 0L; } } /** * Retrieves a float value using the specified key, or 0 if no such key was stored. */ public float getFloat(final String key) { try { return !this.hasKey(key, 99) ? 0.0F : ((NBTBase.NBTPrimitive) this.tagMap.get(key)).getFloat(); } catch (final ClassCastException var3) { return 0.0F; } } /** * Retrieves a double value using the specified key, or 0 if no such key was stored. */ public double getDouble(final String key) { try { return !this.hasKey(key, 99) ? 0.0D : ((NBTBase.NBTPrimitive) this.tagMap.get(key)).getDouble(); } catch (final ClassCastException var3) { return 0.0D; } } /** * Retrieves a string value using the specified key, or an empty string if no such key was stored. */ public String getString(final String key) { try { return !this.hasKey(key, 8) ? "" : this.tagMap.get(key).getString(); } catch (final ClassCastException var3) { return ""; } } /** * Retrieves a byte array using the specified key, or a zero-length array if no such key was stored. */ public byte[] getByteArray(final String key) { try { return !this.hasKey(key, 7) ? new byte[0] : ((NBTTagByteArray) this.tagMap.get(key)).getByteArray(); } catch (final ClassCastException var3) { throw new ReportedException(this.createCrashReport(key, 7, var3)); } } /** * Retrieves an int array using the specified key, or a zero-length array if no such key was stored. */ public int[] getIntArray(final String key) { try { return !this.hasKey(key, 11) ? new int[0] : ((NBTTagIntArray) this.tagMap.get(key)).getIntArray(); } catch (final ClassCastException var3) { throw new ReportedException(this.createCrashReport(key, 11, var3)); } } /** * Retrieves a NBTTagCompound subtag matching the specified key, or a new empty NBTTagCompound if no such key was stored. */ public NBTTagCompound getCompoundTag(final String key) { try { return !this.hasKey(key, 10) ? new NBTTagCompound() : (NBTTagCompound) this.tagMap.get(key); } catch (final ClassCastException var3) { throw new ReportedException(this.createCrashReport(key, 10, var3)); } } /** * Gets the NBTTagList object with the given name. Args: name, NBTBase type */ public NBTTagList getTagList(final String key, final int type) { try { if (this.getTagType(key) != 9) return new NBTTagList(); else { final NBTTagList var3 = (NBTTagList) this.tagMap.get(key); return var3.tagCount() > 0 && var3.getTagType() != type ? new NBTTagList() : var3; } } catch (final ClassCastException var4) { throw new ReportedException(this.createCrashReport(key, 9, var4)); } } /** * Retrieves a boolean value using the specified key, or false if no such key was stored. This uses the getByte method. */ public boolean getBoolean(final String key) { return this.getByte(key) != 0; } /** * Remove the specified tag. */ public void removeTag(final String key) { this.tagMap.remove(key); } @Override public String toString() { String var1 = "{"; String var3; for (final Iterator<String> var2 = this.tagMap.keySet().iterator(); var2.hasNext(); var1 = var1 + var3 + ':' + this.tagMap.get(var3) + ',') var3 = var2.next(); return var1 + "}"; } /** * Return whether this compound has no tags. */ @Override public boolean hasNoTags() { return this.tagMap.isEmpty(); } /** * Create a crash report which indicates a NBT read error. */ private CrashReport createCrashReport(final String key, final int expectedType, final ClassCastException ex) { final CrashReport var4 = CrashReport.makeCrashReport(ex, "Reading NBT data"); final CrashReportCategory var5 = var4.makeCategoryDepth("Corrupt NBT tag", 1); var5.addCrashSectionCallable("Tag type found", new Callable<String>() { private static final String __OBFID = "CL_00001216"; @Override public String call() { return NBTBase.NBT_TYPES[NBTTagCompound.this.tagMap.get(key).getId()]; } }); var5.addCrashSectionCallable("Tag type expected", new Callable<String>() { private static final String __OBFID = "CL_00001217"; @Override public String call() { return NBTBase.NBT_TYPES[expectedType]; } }); var5.addCrashSection("Tag name", key); return var4; } /** * Creates a clone of the tag. */ @Override public NBTBase copy() { final NBTTagCompound var1 = new NBTTagCompound(); final Iterator<String> var2 = this.tagMap.keySet().iterator(); while (var2.hasNext()) { final String var3 = var2.next(); var1.setTag(var3, this.tagMap.get(var3).copy()); } return var1; } @Override public boolean equals(final Object p_equals_1_) { if (super.equals(p_equals_1_)) { final NBTTagCompound var2 = (NBTTagCompound) p_equals_1_; return this.tagMap.entrySet().equals(var2.tagMap.entrySet()); } else return false; } @Override public int hashCode() { return super.hashCode() ^ this.tagMap.hashCode(); } private static void writeEntry(final String name, final NBTBase data, final DataOutput output) throws IOException { output.writeByte(data.getId()); if (data.getId() != 0) { output.writeUTF(name); data.write(output); } } private static byte readType(final DataInput input, final NBTSizeTracker sizeTracker) throws IOException { return input.readByte(); } private static String readKey(final DataInput input, final NBTSizeTracker sizeTracker) throws IOException { return input.readUTF(); } static NBTBase readNBT(final byte id, final String key, final DataInput input, final int depth, final NBTSizeTracker sizeTracker) { final NBTBase var5 = NBTBase.createNewByType(id); try { var5.read(input, depth, sizeTracker); return var5; } catch (final IOException var9) { final CrashReport var7 = CrashReport.makeCrashReport(var9, "Loading NBT data"); final CrashReportCategory var8 = var7.makeCategory("NBT Tag"); var8.addCrashSection("Tag name", key); var8.addCrashSection("Tag type", Byte.valueOf(id)); throw new ReportedException(var7); } } /** * Merges this NBTTagCompound with the given compound. Any sub-compounds are merged using the same methods, other types of tags are overwritten from the given compound. */ public void merge(final NBTTagCompound other) { this.mergeChecked(other); } public boolean mergeChecked(final NBTTagCompound other) { final Iterator<String> it = other.tagMap.keySet().iterator(); boolean modified = false; while (it.hasNext()) { final String key = it.next(); final NBTBase value = other.tagMap.get(key); if (value.getId() == 10) { if (this.hasKey(key, 10)) { final NBTTagCompound var5 = this.getCompoundTag(key); modified |= var5.mergeChecked((NBTTagCompound) value); } else { this.setTag(key, new NBTTagCompound.CopyOnWrite((NBTTagCompound) value)); modified = true; } } else { this.setTag(key, value.copy()); modified = true; } } return modified; } public static class CopyOnWrite extends NBTTagCompound { boolean copied = false; public CopyOnWrite(final NBTTagCompound tag) { super(tag.tagMap); } @Override public void setTag(final String key, final NBTBase value) { this.shallowCopy(); super.setTag(key, value); } @Override public void removeTag(final String key) { if (!this.copied && this.hasKey(key)) this.shallowCopy(); super.removeTag(key); } private void shallowCopy() { if (!this.copied) { this.copied = true; final Map<String, NBTBase> oldMap = this.tagMap; this.tagMap = new PatriciaTrie<>(); this.tagMap.putAll(oldMap); } } @Override public NBTTagCompound getCompoundTag(final String key) { return new CopyOnWrite(super.getCompoundTag(key)); } @Override public NBTBase getTag(final String key) { final NBTBase tag = super.getTag(key); if (tag == null) return null; if (tag.getId() == 9) return new NBTTagList.CopyOnWrite((NBTTagList) tag); else if (tag.getId() == 10) return new CopyOnWrite((NBTTagCompound) tag); else return tag.copy(); } @Override public NBTTagList getTagList(final String key, final int type) { return new NBTTagList.CopyOnWrite(super.getTagList(key, type)); } } }