package net.mcft.copy.backpacks.misc.util; import java.util.Arrays; import java.util.List; import net.minecraft.item.ItemStack; import net.minecraft.nbt.*; /** Contains NBT related methods for manipulating NBT tags and item stacks. */ public final class NbtUtils { private NbtUtils() { } // TODO: Update to use enum instead and make methods "safer" by checking the type before casting. public static final class NbtType { private NbtType() { } public static final int END = 0; public static final int BYTE = 1; public static final int SHORT = 2; public static final int INT = 3; public static final int LONG = 4; public static final int FLOAT = 5; public static final int DOUBLE = 6; public static final int BYTE_ARRAY = 7; public static final int STRING = 8; public static final int LIST = 9; public static final int COMPOUND = 10; public static final int INT_ARRAY = 11; } public static final String TAG_INDEX = "index"; public static final String TAG_STACK = "stack"; // Utility ItemStack / NBT manipulation methods /** Gets an NBT tag of the specified item stack, or null if it doesn't exist. * Example: <pre>{@code StackUtils.get(stack, "display", "color") }</pre> */ public static NBTBase get(ItemStack stack, String... tags) { return get(stack.getTagCompound(), tags); } /** Gets a child NBT tag of the specified compound tag, or null if it doesn't exist. * Example: <pre>{@code StackUtils.get(compound, "display", "color") }</pre> */ public static NBTBase get(NBTTagCompound compound, String... tags) { if (compound == null) return null; String tag = null; for (int i = 0; i < tags.length; i++) { tag = tags[i]; if (!compound.hasKey(tag)) return null; if (i == tags.length - 1) break; compound = compound.getCompoundTag(tag); } return compound.getTag(tag); } /** Gets a value from the specified item stack's compound tag, or the default if it doesn't exist. * Example: <pre>{@code StackUtils.get(stack, -1, "display", "color") }</pre> */ public static <T> T get(ItemStack stack, T defaultValue, String... tags) { return get(stack.getTagCompound(), defaultValue, tags); } /** Gets a value from the specified compound tag, or the default if it doesn't exist. * Example: <pre>{@code StackUtils.get(compound, -1, "display", "color") }</pre> */ public static <T> T get(NBTTagCompound compound, T defaultValue, String... tags) { NBTBase tag = get(compound, tags); return ((tag != null) ? getTagValue(tag) : defaultValue); } /** Returns if the specified item stack's compound tag has a certain NBT tag. * Example: <pre>{@code StackUtils.has(stack, "display", "color") }</pre> */ public static boolean has(ItemStack stack, String... tags) { return has(stack.getTagCompound(), tags); } /** Returns if the specified compound tag has a certain child NBT tag. * Example: <pre>{@code StackUtils.has(compound, "display", "color") }</pre> */ public static boolean has(NBTTagCompound compound, String... tags) { return (get(compound, tags) != null); } /** Adds or replaces a tag on the specified item stack's compound tag, creating it and any parent compound tags if necessary. * Example: <pre>{@code StackUtils.set(stack, new NBTTagInt(0xFF0000), "display", "color") }</pre> */ public static void set(ItemStack stack, NBTBase nbtTag, String... tags) { if (stack.isEmpty()) throw new IllegalArgumentException("stack is empty"); NBTTagCompound compound = stack.getTagCompound(); if (compound == null) stack.setTagCompound(compound = new NBTTagCompound()); set(compound, nbtTag, tags); } /** Adds or replaces a tag on the specified compound tag, creating any parent compound tags if necessary. * Example: <pre>{@code StackUtils.set(compound, new NBTTagInt(0xFF0000), "display", "color") }</pre> */ public static void set(NBTTagCompound compound, NBTBase nbtTag, String... tags) { if (compound == null) throw new IllegalArgumentException("compound is null"); String tag = null; for (int i = 0; i < tags.length; i++) { tag = tags[i]; if (i == tags.length - 1) break; if (!compound.hasKey(tag)) { NBTTagCompound child = new NBTTagCompound(); compound.setTag(tag, child); compound = child; } else compound = compound.getCompoundTag(tag); } compound.setTag(tag, nbtTag); } /** Adds or replaces a value on the specified item stack's compound tag, creating it and any parent compound tags if necessary. * Example: <pre>{@code StackUtils.set(stack, 0xFF0000, "display", "color") }</pre> */ public static <T> void set(ItemStack stack, T value, String... tags) { set(stack, createTag(value), tags); } /** Adds or replaces a value on the specified compound tag, creating any parent compound tags if necessary. * Example: <pre>{@code StackUtils.set(compound, 0xFF0000, "display", "color") }</pre> */ public static <T> void set(NBTTagCompound compound, T value, String... tags) { set(compound, createTag(value), tags); } /** Removes a certain NBT tag from the specified item stack's compound tag. * Example: <pre>{@code StackUtils.remove(stack, "display", "color") }</pre> */ public static void remove(ItemStack stack, String... tags) { if (tags.length == 0) throw new IllegalArgumentException( "tags should have at least one element"); if (!stack.hasTagCompound()) return; NBTTagCompound compound = stack.getTagCompound(); remove(compound, tags); // If compound is empty, remove it from the stack. if (compound.hasNoTags()) stack.setTagCompound(null); } /** Removes a certain NBT tag from the specified compound tag. * Example: <pre>{@code StackUtils.remove(compound, "display", "color") }</pre> */ public static void remove(NBTTagCompound compound, String... tags) { if (tags.length == 0) throw new IllegalArgumentException( "tags should have at least one element"); if (compound == null) return; if (tags.length > 1) { NBTBase tag = compound.getTag(tags[0]); if (!(tag instanceof NBTTagCompound)) return; NBTTagCompound subCompound = (NBTTagCompound)tag; remove(subCompound, (String[])Arrays.copyOfRange(tags, 1, tags.length)); // If subCompound is empty, remove it from the parent compound. if (!subCompound.hasNoTags()) return; } compound.removeTag(tags[0]); } // Compound / List creation /** Creates an NBT compound from the name-value pairs in the parameters. * Doesn't add any null values to the resulting compound tag. * Example: <pre>{@code NbtUtils.createCompound("id", 1, "name", "copygirl") }</pre> */ public static NBTTagCompound createCompound(Object... nameValuePairs) { return addToCompound(new NBTTagCompound(), nameValuePairs); } /** Adds entries to an ItemStack's NBT data from the name-value pairs in the parameters. * Doesn't add any null values to the ItemStack's compound tag. * Example: <pre>{@code NbtUtils.createCompound("id", 1, "name", "copygirl") }</pre> */ public static NBTTagCompound add(ItemStack stack, Object... nameValuePairs) { if (stack.isEmpty()) throw new IllegalArgumentException("stack is empty"); if (!stack.hasTagCompound()) stack.setTagCompound(new NBTTagCompound()); return addToCompound(stack.getTagCompound(), nameValuePairs); } /** Adds entries to an NBT compound from the name-value pairs in the parameters. * Doesn't add any null values to the specified compound tag. * Example: <pre>{@code NbtUtils.addToCompound(compound, "id", 1, "name", "copygirl") }</pre> */ public static NBTTagCompound addToCompound(NBTTagCompound compound, Object... nameValuePairs) { if (compound == null) throw new IllegalArgumentException("compound is null"); for (int i = 0; i < nameValuePairs.length; i += 2) { String name = (String)nameValuePairs[i]; Object value = nameValuePairs[i + 1]; if (value == null) continue; compound.setTag(name, createTag(value)); } return compound; } /** Creates an NBT list with the values, all of the single type. * Doesn't add any null values to the resulting list tag. */ public static NBTTagList createList(Object... values) { return addToList(new NBTTagList(), values); } /** Adds values to an NBT list. Doesn't add any null values to the specified list tag. */ public static NBTTagList addToList(NBTTagList list, Object... values) { if (list == null) throw new IllegalArgumentException("list is null"); for (Object value : values) { if (value == null) continue; list.appendTag(createTag(value)); } return list; } // Reading / writing ItemStacks /** Writes an item stack to an NBT compound. */ public static NBTTagCompound writeItem(ItemStack item) { return writeItem(item, true); } /** Writes an item stack to an NBT compound. */ public static NBTTagCompound writeItem(ItemStack item, boolean writeNullAsEmptyCompound) { return (!item.isEmpty()) ? item.writeToNBT(new NBTTagCompound()) : (writeNullAsEmptyCompound ? new NBTTagCompound() : null); } /** Reads an item stack from an NBT compound. */ public static ItemStack readItem(NBTTagCompound compound) { return ((compound != null) && !compound.hasNoTags()) ? new ItemStack(compound) : ItemStack.EMPTY; } /** Writes an item stack array to an NBT list. */ public static NBTTagList writeItems(ItemStack[] items) { NBTTagList list = new NBTTagList(); for (int i = 0; i < items.length; i++) if (items[i] != null) list.appendTag(createCompound( TAG_INDEX, (short)i, TAG_STACK, writeItem(items[i]))); return list; } /** Reads items from an NBT list to an item stack array. */ public static ItemStack[] readItems(NBTTagList list, ItemStack[] items) { return readItems(list, items, null); } /** Reads items from an NBT list to an item stack array. * Any items falling outside the range of the items array * will get added to the invalid list if that's non-null. */ public static ItemStack[] readItems(NBTTagList list, ItemStack[] items, List<ItemStack> invalid) { for (int i = 0; i < list.tagCount(); i++) { NBTTagCompound compound = list.getCompoundTagAt(i); int index = compound.getShort(TAG_INDEX); ItemStack stack = readItem(compound.getCompoundTag(TAG_STACK)); if ((index >= 0) || (index < items.length)) items[index] = stack; else if (invalid != null) invalid.add(stack); } return items; } // Other utility functions /** Returns the primitive value of a tag, casted to the return type. */ @SuppressWarnings("unchecked") public static <T> T getTagValue(NBTBase tag) { if (tag == null) throw new IllegalArgumentException("tag is null"); if (tag instanceof NBTTagByte) return (T)(Object)((NBTTagByte)tag).getByte(); if (tag instanceof NBTTagShort) return (T)(Object)((NBTTagShort)tag).getShort(); if (tag instanceof NBTTagInt) return (T)(Object)((NBTTagInt)tag).getInt(); if (tag instanceof NBTTagLong) return (T)(Object)((NBTTagLong)tag).getLong(); if (tag instanceof NBTTagFloat) return (T)(Object)((NBTTagFloat)tag).getFloat(); if (tag instanceof NBTTagDouble) return (T)(Object)((NBTTagDouble)tag).getDouble(); if (tag instanceof NBTTagString) return (T)(Object)((NBTTagString)tag).getString(); if (tag instanceof NBTTagByteArray) return (T)((NBTTagByteArray)tag).getByteArray(); if (tag instanceof NBTTagIntArray) return (T)((NBTTagIntArray)tag).getIntArray(); throw new IllegalArgumentException(NBTBase.NBT_TYPES[tag.getId()] + " isn't a primitive NBT tag"); } /** Creates and returns a primitive NBT tag from a value. * If the value already is an NBT tag, it is returned instead. */ public static NBTBase createTag(Object value) { if (value == null) throw new IllegalArgumentException("value is null"); if (value instanceof NBTBase) return (NBTBase)value; if (value instanceof Byte) return new NBTTagByte((Byte)value); if (value instanceof Short) return new NBTTagShort((Short)value); if (value instanceof Integer) return new NBTTagInt((Integer)value); if (value instanceof Long) return new NBTTagLong((Long)value); if (value instanceof Float) return new NBTTagFloat((Float)value); if (value instanceof Double) return new NBTTagDouble((Double)value); if (value instanceof String) return new NBTTagString((String)value); if (value instanceof byte[]) return new NBTTagByteArray((byte[])value); if (value instanceof int[]) return new NBTTagIntArray((int[])value); throw new IllegalArgumentException("Can't create an NBT tag of value: " + value); } }