Classe Java pour gérer le format NBT
Le contenu de cette page n'est pas cautionné par Mojang, le Minecraft Wiki, le Canal IRC Minecraft ni le Forum Minecraft. |
Cette classe Java lit une structure NBT et retourne le plus haut tag d'un InputStream (classe abstraite permettant de lire les flux d'octets) et permet d'écrire une structure NBT à travers le plus haut tag d'un OutputStream (classe abstraite permettant d'écrire des flux d'octets). Une documentation condensée se trouve au bas de la page.
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; /** * NBT IO class * * @see <a href="http://www.minecraft.net/docs/NBT.txt">Online NBT specification</a> */ public class Tag { private final Type type; private Type listType = null; private final String name; private Object value; /** * Enum for the tag types. */ public enum Type { TAG_End, TAG_Byte, TAG_Short, TAG_Int, TAG_Long, TAG_Float, TAG_Double, TAG_Byte_Array, TAG_String, TAG_List, TAG_Compound } /** * Create a new TAG_List or TAG_Compound NBT tag. * * @param type either TAG_List or TAG_Compound * @param name name for the new tag or <code>null</code> to create an unnamed tag. * @param value list of tags to add to the new tag. */ public Tag(Type type, String name, Tag[] value) { this(type, name, (Object) value); } /** * Create a new TAG_List with an empty list. Use {@link Tag#addTag(Tag)} to add tags later. * * @param name name for this tag or <code>null</code> to create an unnamed tag. * @param listType type of the elements in this empty list. */ public Tag(String name, Type listType) { this(Type.TAG_List, name, listType); } /** * Create a new NBT tag. * * @param type any value from the <code>{@link Type}</code> enum. * @param name name for the new tag or <code>null</code> to create an unnamed tag. * @param value an object that fits the tag type or a <code>{@link Type}</code> to create an empty TAG_List with this list type. */ public Tag(Type type, String name, Object value) { if (type == Type.TAG_Compound) if (!(value instanceof Tag[])) throw new IllegalArgumentException(); switch (type) { case TAG_End: if (value != null) throw new IllegalArgumentException(); break; case TAG_Byte: if (!(value instanceof Byte)) throw new IllegalArgumentException(); break; case TAG_Short: if (!(value instanceof Short)) throw new IllegalArgumentException(); break; case TAG_Int: if (!(value instanceof Integer)) throw new IllegalArgumentException(); break; case TAG_Long: if (!(value instanceof Long)) throw new IllegalArgumentException(); break; case TAG_Float: if (!(value instanceof Float)) throw new IllegalArgumentException(); break; case TAG_Double: if (!(value instanceof Double)) throw new IllegalArgumentException(); break; case TAG_Byte_Array: if (!(value instanceof byte[])) throw new IllegalArgumentException(); break; case TAG_String: if (!(value instanceof String)) throw new IllegalArgumentException(); break; case TAG_List: if (value instanceof Type) { this.listType = (Type) value; value = new Tag[0]; } else { if (!(value instanceof Tag[])) throw new IllegalArgumentException(); this.listType = (((Tag[]) value)[0]).getType(); } break; case TAG_Compound: if (!(value instanceof Tag[])) throw new IllegalArgumentException(); break; default: throw new IllegalArgumentException(); } this.type = type; this.name = name; this.value = value; } public Type getType() { return type; } public String getName() { return name; } public Object getValue() { return value; } public Type getListType() { return listType; } /** * Add a tag to a TAG_List or a TAG_Compound. */ public void addTag(Tag tag) { if (type != Type.TAG_List && type != Type.TAG_Compound) throw new RuntimeException(); Tag[] subtags = (Tag[]) value; insertTag(tag, subtags.length); } /** * Add a tag to a TAG_List or a TAG_Compound at the specified index. */ public void insertTag(Tag tag, int index) { if (type != Type.TAG_List && type != Type.TAG_Compound) throw new RuntimeException(); Tag[] subtags = (Tag[]) value; if (subtags.length > 0) if (type == Type.TAG_List && tag.getType() != getListType()) throw new IllegalArgumentException(); if (index > subtags.length) throw new IndexOutOfBoundsException(); Tag[] newValue = new Tag[subtags.length + 1]; System.arraycopy(subtags, 0, newValue, 0, index); newValue[index] = tag; System.arraycopy(subtags, index, newValue, index + 1, subtags.length - index); value = newValue; } /** * Remove a tag from a TAG_List or a TAG_Compound at the specified index. * * @return the removed tag */ public Tag removeTag(int index) { if (type != Type.TAG_List && type != Type.TAG_Compound) throw new RuntimeException(); Tag[] subtags = (Tag[]) value; Tag victim = subtags[index]; Tag[] newValue = new Tag[subtags.length - 1]; System.arraycopy(subtags, 0, newValue, 0, index); index++; System.arraycopy(subtags, index, newValue, index - 1, subtags.length - index); value = newValue; return victim; } /** * Remove a tag from a TAG_List or a TAG_Compound. If the tag is not a child of this tag then nested tags are searched. * * @param tag tag to look for */ public void removeSubTag(Tag tag) { if (type != Type.TAG_List && type != Type.TAG_Compound) throw new RuntimeException(); if (tag == null) return; Tag[] subtags = (Tag[]) value; for (int i = 0; i < subtags.length; i++) { if (subtags[i] == tag) { removeTag(i); return; } else { if (subtags[i].type == Type.TAG_List || subtags[i].type == Type.TAG_Compound) { subtags[i].removeSubTag(tag); } } } } /** * Find the first nested tag with specified name in a TAG_Compound. * * @param name the name to look for. May be <code>null</code> to look for unnamed tags. * @return the first nested tag that has the specified name. */ public Tag findTagByName(String name) { return findNextTagByName(name, null); } /** * Find the first nested tag with specified name in a TAG_List or TAG_Compound after a tag with the same name. * * @param name the name to look for. May be <code>null</code> to look for unnamed tags. * @param found the previously found tag with the same name. * @return the first nested tag that has the specified name after the previously found tag. */ public Tag findNextTagByName(String name, Tag found) { if (type != Type.TAG_List && type != Type.TAG_Compound) return null; Tag[] subtags = (Tag[]) value; for (Tag subtag : subtags) { if ((subtag.name == null && name == null) || (subtag.name != null && subtag.name.equals(name))) { return subtag; } else { Tag newFound = subtag.findTagByName(name); if (newFound != null) if (newFound == found) continue; else return newFound; } } return null; } /** * Read a tag and its nested tags from an InputStream. * * @param is stream to read from, like a FileInputStream * @return NBT tag or structure read from the InputStream * @throws IOException if there was no valid NBT structure in the InputStream or if another IOException occurred. */ public static Tag readFrom(InputStream is) throws IOException { DataInputStream dis = new DataInputStream(new GZIPInputStream(is)); byte type = dis.readByte(); Tag tag = null; if (type == 0) { tag = new Tag(Type.TAG_End, null, null); } else { tag = new Tag(Type.values()[type], dis.readUTF(), readPayload(dis, type)); } dis.close(); return tag; } private static Object readPayload(DataInputStream dis, byte type) throws IOException { switch (type) { case 0: return null; case 1: return dis.readByte(); case 2: return dis.readShort(); case 3: return dis.readInt(); case 4: return dis.readLong(); case 5: return dis.readFloat(); case 6: return dis.readDouble(); case 7: int length = dis.readInt(); byte[] ba = new byte[length]; dis.readFully(ba); return ba; case 8: return dis.readUTF(); case 9: byte lt = dis.readByte(); int ll = dis.readInt(); Tag[] lo = new Tag[ll]; for (int i = 0; i < ll; i++) { lo[i] = new Tag(Type.values()[lt], null, readPayload(dis, lt)); } if (lo.length == 0) return Type.values()[lt]; else return lo; case 10: byte stt; Tag[] tags = new Tag[0]; do { stt = dis.readByte(); String name = null; if (stt != 0) { name = dis.readUTF(); } Tag[] newTags = new Tag[tags.length + 1]; System.arraycopy(tags, 0, newTags, 0, tags.length); newTags[tags.length] = new Tag(Type.values()[stt], name, readPayload(dis, stt)); tags = newTags; } while (stt != 0); return tags; } return null; } /** * Read a tag and its nested tags from an InputStream. * * @param os stream to write to, like a FileOutputStream * @throws IOException if this is not a valid NBT structure or if any IOException occurred. */ public void writeTo(OutputStream os) throws IOException { GZIPOutputStream gzos; DataOutputStream dos = new DataOutputStream(gzos = new GZIPOutputStream(os)); dos.writeByte(type.ordinal()); if (type != Type.TAG_End) { dos.writeUTF(name); writePayload(dos); } gzos.flush(); gzos.close(); } private void writePayload(DataOutputStream dos) throws IOException { switch (type) { case TAG_End: break; case TAG_Byte: dos.writeByte((Byte) value); break; case TAG_Short: dos.writeShort((Short) value); break; case TAG_Int: dos.writeInt((Integer) value); break; case TAG_Long: dos.writeLong((Long) value); break; case TAG_Float: dos.writeFloat((Float) value); break; case TAG_Double: dos.writeDouble((Double) value); break; case TAG_Byte_Array: byte[] ba = (byte[]) value; dos.writeInt(ba.length); dos.write(ba); break; case TAG_String: dos.writeUTF((String) value); break; case TAG_List: Tag[] list = (Tag[]) value; dos.writeByte(getListType().ordinal()); dos.writeInt(list.length); for (Tag tt : list) { tt.writePayload(dos); } break; case TAG_Compound: Tag[] subtags = (Tag[]) value; for (Tag st : subtags) { Tag subtag = st; Type type = subtag.getType(); dos.writeByte(type.ordinal()); if (type != Type.TAG_End) { dos.writeUTF(subtag.getName()); subtag.writePayload(dos); } } break; } } /** * Print the NBT structure to System.out */ public void print() { print(this, 0); } private String getTypeString(Type type) { switch (type) { case TAG_End: return "TAG_End"; case TAG_Byte: return "TAG_Byte"; case TAG_Short: return "TAG_Short"; case TAG_Int: return "TAG_Int"; case TAG_Long: return "TAG_Long"; case TAG_Float: return "TAG_Float"; case TAG_Double: return "TAG_Double"; case TAG_Byte_Array: return "TAG_Byte_Array"; case TAG_String: return "TAG_String"; case TAG_List: return "TAG_List"; case TAG_Compound: return "TAG_Compound"; } return null; } private void indent(int indent) { for (int i = 0; i < indent; i++) { System.out.print(" "); } } private void print(Tag t, int indent) { Type type = t.getType(); if (type == Type.TAG_End) return; String name = t.getName(); indent(indent); System.out.print(getTypeString(t.getType())); if (name != null) System.out.print("(\"" + t.getName() + "\")"); if (type == Type.TAG_Byte_Array) { byte[] b = (byte[]) t.getValue(); System.out.println(": [" + b.length + " bytes]"); } else if (type == Type.TAG_List) { Tag[] subtags = (Tag[]) t.getValue(); System.out.println(": " + subtags.length + " entries of type " + getTypeString(t.getListType())); for (Tag st : subtags) { print(st, indent + 1); } indent(indent); System.out.println("}"); } else if (type == Type.TAG_Compound) { Tag[] subtags = (Tag[]) t.getValue(); System.out.println(": " + (subtags.length - 1) + " entries"); indent(indent); System.out.println("{"); for (Tag st : subtags) { print(st, indent + 1); } indent(indent); System.out.println("}"); } else { System.out.println(": " + t.getValue()); } } // For testing purposes. public static void main(String[] args) { try { String filename = "bigtest.nbt"; if (args.length > 0) filename = args[0]; Tag test = Tag.readFrom(new FileInputStream(filename)); FileOutputStream fos = new FileOutputStream("bigtestwrite.nbt"); test.writeTo(fos); fos.close(); test = Tag.readFrom(new FileInputStream("bigtestwrite.nbt")); test.print(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
Si vous trouvez des bugs : corrigez cette page.
Si vous n'aimez pas ce code, peut-être que le projet JNBT vous intéressera. La classe ci-dessus n'est pas liée à JNBT.
Documentation[modifier | modifier le wikicode]
Voici les fonctions utilisées par la classe ci-dessus ainsi que leur documentation, sous forme condensée. Les fonctions privées n'y sont pas incluses. Les fonctions sont divisées en 4 catégories : constructeurs, assesseurs, manipulation de tags et entrée/sortie. Le but de cette documentation est d'aider les lecteurs à comprendre comment ça fonctionne et de leur permettre d'utiliser la classe Tag.
Constructeurs :
- Tag(Type type,String name,Tag[] value)
- Description : Créée un nouveau TAG_List ou un nouveau tag NBT TAG_Compound.
- Paramètre type : Soit TAG_List, soit TAG_Compound.
- Paramètre name : Nom du nouveau tag ou null pour créer un tag sans nom.
- Paramètre value : Liste des tags à ajouter au nouveau tag.
- Tag(String name, Type listType)
- Description : Créée un nouveau TAG_List avec une liste vide. Utilisez la fonction addTag(Tag tag) pour y rajouter des tags par la suite.
- Paramètre name : Nom de ce tag ou null pour créer un tag sans nom.
- Paramètre listType : Type des éléments dans cette liste vide.
- Tag(Type type, String name, Object value)
- Description : Créée un nouveau tag NBT.
- Paramètre type : N'importe quelle valeur de l'énumération Type.
- Paramètre name : Nom du nouveau tag ou null pour créer un tag sans nom.
- Paramètre value : Un objet qui correspond au type de tag ou un élément de l'énumération Type pour créer un TAG_List vide avec ce type de liste.
Accesseurs :
- getType()
- getName()
- getValue()
- getListType()
Manipulation de tags :
- addTag(Tag tag)
- Description : Ajoute un tag à un TAG_List ou à un TAG_Compound.
- insertTag(Tag tag, int index)
- Description : Ajoute un tag à un TAG_List ou à un TAG_Compound à l'index spécifié.
- removeTag(int index)
- Description: Retire un tag d'un TAG_List ou d'un TAG_Compound à l'index spécifié.
- valeur de retour : Le tag retiré.
- removeSubTag(Tag tag)
- Description : Retire un tag d'un TAG_List ou d'un TAG_Compound. Si le tag n'hérite pas de ce tag, alors une recherche est effectuée sur les tags imbriqués.
- findTagByName(String name)
- Description : Trouve le premier tag imbriqué ayant le nom spécifié dans un TAG_Compound.
- Paramètre name : Le nom à rechercher. Peut valoir null pour chercher des tags sans nom.
- Valeur de retour : Le premier tag imbriqué ayant le nom spécifié.
- findNextTagByName(String name, Tag found)
- Description : Trouve le premier tag imbriqué ayant le nom spécifié dans un TAG_List ou dans un TAG_Compound après un tag ayant le même nom.
- Paramètre name : Le nom à rechercher. Peut valoir null pour chercher des tags sans nom.
- Paramètre found : Le tag ayant le même nom précédemment trouvé.
- Valeur de retour : Le premier tag imbriqué ayant le nom spécifié après le tag précédemment trouvé.
Entrée/sortie :
- readFrom(InputStream is)
- Description : Lit un tag et ses tags imbriqués à partir d'un InputStream.
- Paramètre is : Le flux à lire, un FileInputStream par exemple.
- Valeur de retour : Tag NBT ou structure lue à partir du flux spécifié.
- Exception : IOException s'il n'y avait aucune structure NBT valide dans le flux ou si une autre IOException est levée.
- writeTo(OutputStream os)
- Description : Ecrit un tag et ses tags imbriqués dans un OutputStream.
- Paramètre os : Le flux sur lequel écrire, un FileOutputStream par exemple.
- Exception : IOException si ce n'est pas une structure NBT valide ou si une autre IOException est levée.
- print()
- Description : Envoie la structure NBT sur le flux System.out. Print the NBT structure to System.out.