heavily refactored Tag to be type safe and extendable: every tag has now one type entry and one own class, nothing else.

master
flying sheep 2013-04-04 22:36:36 +02:00
parent e79f8856e3
commit 7d47a3ea85
19 changed files with 742 additions and 514 deletions

View File

@ -1,31 +1,25 @@
package MoF;
import amidst.Util;
import amidst.nbt.Tag;
import amidst.nbt.TagCompound;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.*;
import java.util.ArrayList;
import javax.swing.filechooser.FileFilter;
public class SaveLoader {
public static String genType = "default";
public static FileFilter getFilter() {
return (new FileFilter() {
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String[] st = f.getName().split("\\/");
if (st[st.length-1].toLowerCase().equals("level.dat"))
return true;
return false;
if (f.isDirectory()) {
return true;
}
String[] st = f.getName().split("\\/");
return st[st.length - 1].equalsIgnoreCase("level.dat");
}
@Override
public String getDescription() {
return "Minecraft Data File (level.dat)";
@ -60,20 +54,19 @@ public class SaveLoader {
return players;
}
public void movePlayer(String name, int x, int y) {
File out = null;
File out;
if (multi) {
String outpath = file.getParent() + "/players/" + name +".dat";
out = new File(outpath);
String outPath = file.getParent() + "/players/" + name +".dat";
out = new File(outPath);
backupFile(out);
try {
Tag t = Tag.readFrom(new FileInputStream(out));
TagCompound t = Tag.readFrom(new FileInputStream(out));
Tag pos = t.findTagByName("Pos");
Tag[] pa = (Tag[]) pos.getValue();
pa[0].setValue((double)x);
pa[1].setValue((double)120);
pa[2].setValue((double)y);
Tag<Double>[] pa = (Tag[]) pos.getValue();
pa[0].setValue((double) x);
pa[1].setValue((double) 120);
pa[2].setValue((double) y);
t.writeTo(new FileOutputStream(out));
} catch (Exception e) {
e.printStackTrace();
}
@ -82,35 +75,33 @@ public class SaveLoader {
out = file;
backupFile(out);
try {
Tag t = Tag.readFrom(new FileInputStream(out));
TagCompound t = Tag.readFrom(new FileInputStream(out));
Tag pos = t.findTagByName("Pos");
Tag[] pa = (Tag[]) pos.getValue();
pa[0].setValue((double)x);
pa[1].setValue((double)120);
pa[2].setValue((double)y);
Tag<Double>[] pa = (Tag[]) pos.getValue();
pa[0].setValue((double) x);
pa[1].setValue((double) 120);
pa[2].setValue((double) y);
t.writeTo(new FileOutputStream(out));
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void backupFile(File f) {
File inputFile = f;
File outputFile = new File(f.toString() + ".moth");
if (!back.contains(outputFile.toString())) {
try {
FileReader in = new FileReader(inputFile);
FileWriter out = new FileWriter(outputFile);
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();
out.close();
back.add(outputFile.toString());
} catch (Exception e) {
}
private void backupFile(File inputFile) {
File outputFile = new File(inputFile.toString() + ".moth");
if (!back.contains(outputFile.toString())) {
try {
FileReader in = new FileReader(inputFile);
FileWriter out = new FileWriter(outputFile);
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();
out.close();
back.add(outputFile.toString());
} catch (Exception ignored) {}
}
}
@ -119,35 +110,34 @@ public class SaveLoader {
players = new ArrayList<Player>();
back = new ArrayList<String>();
try {
Tag t = Tag.readFrom(new FileInputStream(f));
Tag pTag = t.findTagByName("Player");
seed = (Long)t.findTagByName("RandomSeed").getValue();
genType = (String)t.findTagByName("generatorName").getValue();
TagCompound t = Tag.readFrom(new FileInputStream(f));
TagCompound pTag = (TagCompound) t.findTagByName("Player");
seed = (Long) t.findTagByName("RandomSeed").getValue();
genType = (String) t.findTagByName("generatorName").getValue();
System.out.println("Gen Type: " + genType);
if (pTag!=null) {
if (pTag != null) {
multi = false;
Tag pos = pTag.findTagByName("Pos");
Tag[] pa = (Tag[]) pos.getValue();
double x = (Double) pa[0].getValue();
double z = (Double) pa[2].getValue();
players.add(new Player("Player", (int)x, (int)z));
Tag<Double>[] pa = (Tag[]) pos.getValue();
double x = pa[0].getValue();
double z = pa[2].getValue();
players.add(new Player("Player", (int) x, (int) z));
} else {
multi = true;
File[] listing = new File(f.getParent() + "/players").listFiles();
Tag ps;
for (int i = 0; i < listing.length; i++) {
TagCompound ps;
for (int i = 0; i < (listing != null ? listing.length : 0); i++) {
ps = Tag.readFrom(new FileInputStream(listing[i]));
Tag pos = ps.findTagByName("Pos");
Tag[] pa = (Tag[]) pos.getValue();
double x = (Double) pa[0].getValue();
double z = (Double) pa[2].getValue();
players.add(new Player(listing[i].getName().split("\\.")[0], (int)x, (int)z));
Tag<Double>[] pa = (Tag[]) pos.getValue();
double x = pa[0].getValue();
double z = pa[2].getValue();
players.add(new Player(listing[i].getName().split("\\.")[0], (int) x, (int) z));
}
}
} catch (Exception e) {
// FIXME Dialog when file read fails.
Util.showError(e);
}
}
}

33
src/amidst/Util.java Normal file
View File

@ -0,0 +1,33 @@
package amidst;
import javax.swing.*;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
public class Util {
/** Shows an error message for an exception
* @param e the exception for which the stachtrace is to be shown
*/
public static void showError(Exception e) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
e.printStackTrace(ps);
String trace = baos.toString();
e.printStackTrace();
JOptionPane.showMessageDialog(
null,
trace,
e.toString(),
JOptionPane.ERROR_MESSAGE);
}
// public static void main(String[] args) {
// try {
// int infinity = 1 / 0;
// } catch (Exception e) {
// showError(e);
// }
// }
}

View File

@ -0,0 +1,92 @@
package amidst.nbt;
import java.io.PrintStream;
public abstract class SequenceTagBase<T> extends Tag<Tag<T>[]> {
SequenceTagBase(Type type, String name, Tag<T>[] value) {
super(type, name, value);
}
public abstract void addTag(Tag<T> tag);
/** Add a tag to a TAG_List or a TAG_Compound at the specified index.
*/
public void insertTag(Tag<T> tag, int index) {
if (value.length > 0 && (this instanceof TagList && tag.type != ((TagList) this).listType))
throw new IllegalArgumentException();
if (index > value.length)
throw new IndexOutOfBoundsException();
Tag<T>[] newValue = new Tag[value.length + 1];
System.arraycopy(value, 0, newValue, 0, index);
newValue[index] = tag;
System.arraycopy(value, index, newValue, index + 1, value.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<T> removeTag(int index) {
if (type != Type.TAG_List && type != Type.TAG_Compound)
throw new RuntimeException();
Tag<T> victim = value[index];
Tag<T>[] newValue = new Tag[value.length - 1];
System.arraycopy(value, 0, newValue, 0, index);
index++;
System.arraycopy(value, index, newValue, index - 1, value.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<T> tag) {
if (tag == null) return;
for (int i = 0; i < value.length; i++) {
if (value[i] == tag) {
removeTag(i);
return;
} else {
if (value[i] instanceof SequenceTagBase) {
((SequenceTagBase<T>) value[i]).removeSubTag(tag);
}
}
}
}
/**
* 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 null 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<T> findNextTagByName(String name, Tag<T> found) {
for (Tag<T> subTag : value) {
if ((subTag.name == null && name == null) //End tag
|| (subTag.name != null && subTag.name.equals(name))) {
return subTag;
} else if (subTag instanceof TagCompound) {
Tag<T> newFound = ((TagCompound) subTag).findTagByName(name);
if (newFound != null && newFound != found)
return newFound;
}
}
return null;
}
void serializeEntries(PrintStream ps, int indent) {
serializeIndented(ps, indent, "{");
for (Tag st : value)
st.serialize(ps, indent + 1);
serializeIndented(ps, indent, "}");
}
}

View File

@ -1,10 +1,10 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@ -13,308 +13,68 @@ import java.util.zip.GZIPOutputStream;
*
* @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;
public abstract class Tag<T> {
public final Type type;
public final String name;
T 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,
TAG_Int_Array
}
/**
* 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 null 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 null 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 {@link Type} enum.
* @param name name for the new tag or null to create an unnamed tag.
* @param value an object that fits the tag type or a {@link Type} to create an empty TAG_List with this list type.
*/
public Tag(Type type, String name, Object value) {
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();
//TODO: Int_Array
TAG_End(TagEnd.class),
TAG_Byte(TagByte.class),
TAG_Short(TagShort.class),
TAG_Int(TagInt.class),
TAG_Long(TagLong.class),
TAG_Float(TagFloat.class),
TAG_Double(TagDouble.class),
TAG_Byte_Array(TagByteArray.class),
TAG_String(TagString.class),
TAG_List(TagList.class),
TAG_Compound(TagCompound.class),
TAG_Int_Array(TagIntArray.class);
final Class<? extends Tag> clazz;
private Type(Class<? extends Tag> clazz) {
this.clazz = clazz;
}
public Tag readFrom(String name, DataInputStream dis) {
try {
Constructor<? extends Tag> c = clazz.getDeclaredConstructor(String.class, DataInputStream.class);
return c.newInstance(name, dis);
} catch (NoSuchMethodException e) { //TODO: maybe java 7 for multicatch?
throw new RuntimeException(name, e);
} catch (InvocationTargetException e) {
throw new RuntimeException(name, e);
} catch (InstantiationException e) {
throw new RuntimeException(name, e);
} catch (IllegalAccessException e) {
throw new RuntimeException(name, e);
}
}
public static Type fromByte(Byte type) {
return Type.values()[type];
}
}
Tag(Type type, String name, T value) {
this.type = type;
this.name = name;
this.value = value;
}
public Type getType() {
return type;
}
public String getName() {
return name;
}
public Object getValue() {
public T getValue() {
return value;
}
public void setValue(Object newValue) {
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();
//TODO: Int_Array
}
public void setValue(T newValue) {
value = newValue;
}
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;
int index = subtags.length;
//For TAG_Compund entries, we need to add the tag BEFORE the end,
//or the new tag gets placed after the TAG_End, messing up the data.
//TAG_End MUST be kept at the very end of the TAG_Compound.
if(type == Type.TAG_Compound) index--;
insertTag(tag, index);
}
/**
* 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 null 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 null 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.
*
@ -322,76 +82,17 @@ public class Tag {
* @return NBT tag or structure read from the InputStream
* @throws java.io.IOException if there was no valid NBT structure in the InputStream or if another IOException occurred.
*/
public static Tag readFrom(InputStream is) throws IOException {
public static TagCompound readFrom(InputStream is) throws IOException {
DataInputStream dis = new DataInputStream(new GZIPInputStream(is));
byte type = dis.readByte();
Tag tag;
Type type = Type.fromByte(dis.readByte());
if (type != Type.TAG_Compound)
throw new IOException("Root tags have to be Compound tags");
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;
TagCompound root = (TagCompound) Type.TAG_Compound.readFrom(dis.readUTF(), dis);
is.close();
return root;
}
private static Object readPayload(DataInputStream dis, byte type) throws IOException {
switch (Type.values()[type]) {
case TAG_End:
return null;
case TAG_Byte:
return dis.readByte();
case TAG_Short:
return dis.readShort();
case TAG_Int:
return dis.readInt();
case TAG_Long:
return dis.readLong();
case TAG_Float:
return dis.readFloat();
case TAG_Double:
return dis.readDouble();
case TAG_Byte_Array:
int length = dis.readInt();
byte[] ba = new byte[length];
dis.readFully(ba);
return ba;
case TAG_String:
return dis.readUTF();
case TAG_List:
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 TAG_Compound:
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;
//TODO: Int_Array
}
return null;
}
/**
* Read a tag and its nested tags from an InputStream.
*
@ -409,106 +110,50 @@ public class Tag {
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;
//TODO: Int_Array
}
}
/**
* Print the NBT structure to System.out
abstract void writePayload(DataOutputStream dos) throws IOException;
/** Print the NBT structure to System.out
*/
public void print() {
print(this, 0);
public void serialize() {
serialize(System.out, 0);
}
/** Prints an indented sequence of objects to a PrintStream, terminated by a newline
* @param ps PrintStream to serialize to
* @param indent Indentation level (2 spaces per level are printed)
* @param line Things to serialize after the indentation
*/
static void serializeIndented(PrintStream ps, int indent, Object... line) {
for (int i = 0; i < indent; i++)
ps.print(" ");
for (Object thing : line)
if (thing != null)
if (thing instanceof Object[])
for (Object underThing : (Object[]) thing)
ps.print(underThing);
else
ps.print(thing);
ps.println();
}
private void indent(int indent) {
for (int i = 0; i < indent; i++) {
System.out.print(" ");
}
/** Prints this Tags information and the sequence of Objects to a Prinstream, terminated by a newline
* @param ps PrintStream to serialize to
* @param indent Indentation level (2 spaces per level are printed)
* @param line Things to serialize after the indentation and prefix
*/
void serializePrefix(PrintStream ps, int indent, Object... line) {
if (type == Type.TAG_End) return;
String suffix = (name == null) ? null : "(\"" + name + "\")";
serializeIndented(ps, indent, type, suffix, line);
}
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(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 " + 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());
}
/** Serializes this Tag to a PrintStream
* Default implementation for value-like Tags. Overridden in sequence Tags
* @param ps PrintStream to serialize to
* @param indent Indentation level (2 spaces per level are printed)
*/
void serialize(PrintStream ps, int indent) {
serializePrefix(ps, indent, ": ", value);
}
}

View File

@ -0,0 +1,19 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TagByte extends Tag<Byte> {
TagByte(String name, byte value) {
super(Type.TAG_Byte, name, value);
}
TagByte(String name, DataInputStream dis) throws IOException {
this(name, dis.readByte());
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeByte(value);
}
}

View File

@ -0,0 +1,33 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class TagByteArray extends Tag<byte[]> {
TagByteArray(String name, byte[] value) {
super(Type.TAG_Byte_Array, name, value);
}
TagByteArray(String name, DataInputStream dis) throws IOException {
this(name, readPayload(dis));
}
private static byte[] readPayload(DataInputStream dis) throws IOException {
int length = dis.readInt();
byte[] ba = new byte[length];
dis.readFully(ba);
return ba;
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeInt(value.length);
dos.write(value);
}
@Override
void serialize(PrintStream ps, int indent) {
serializePrefix(ps, indent, ": [" + value.length + " bytes]");
}
}

View File

@ -0,0 +1,74 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
public class TagCompound extends SequenceTagBase<Object> {
/**
* Create a new TAG_Compound NBT tag.
*
* @param name name for the new tag or null to create an unnamed tag.
* @param value list of tags to add to the new tag.
*/
public TagCompound(String name, Tag[] value) {
super(Type.TAG_Compound, name, value);
}
TagCompound(String name, DataInputStream dis) throws IOException {
this(name, readPayload(dis));
}
private static Tag[] readPayload(DataInputStream dis) throws IOException {
Type stt;
List<Tag> tags = new ArrayList<Tag>();
do {
stt = Type.fromByte(dis.readByte());
String name = null;
if (stt != Type.TAG_End) {
name = dis.readUTF();
}
tags.add(stt.readFrom(name, dis));
} while (stt != Type.TAG_End);
return tags.toArray(new Tag[tags.size()]);
}
void writePayload(DataOutputStream dos) throws IOException {
for (Tag subTag : value) {
dos.writeByte(type.ordinal());
if (subTag.type != Type.TAG_End) {
dos.writeUTF(subTag.name);
subTag.writePayload(dos);
}
}
}
/** Add a tag to a TAG_Compound.
*
* We need to add the tag BEFORE the end, or the new tag gets placed after the TAG_End, messing up the data.
* TAG_End MUST be kept at the very end of the TAG_Compound.
*/
public void addTag(Tag tag) {
insertTag(tag, value.length - 1);
}
/**
* Find the first nested tag with specified name in a TAG_Compound.
*
* @param name the name to look for. May be null to look for unnamed tags.
* @return the first nested tag that has the specified name.
*/
public Tag findTagByName(String name) {
return findNextTagByName(name, null);
}
@Override
void serialize(PrintStream ps, int indent) {
serializePrefix(ps, indent, ": ", value.length - 1, " entries");
serializeEntries(ps, indent);
}
}

View File

@ -0,0 +1,19 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TagDouble extends Tag<Double> {
TagDouble(String name, double value) {
super(Type.TAG_Double, name, value);
}
TagDouble(String name, DataInputStream dis) throws IOException {
this(name, dis.readDouble());
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeDouble(value);
}
}

View File

@ -0,0 +1,24 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TagEnd extends Tag<Object> {
public TagEnd(String name) {
super(Type.TAG_End, name, null);
}
TagEnd(String name, DataInputStream dis) throws IOException {
this(name);
}
void writePayload(DataOutputStream dos) throws IOException {}
@Override
public void setValue(Object newValue) {
if (value != null)
throw new IllegalArgumentException();
value = newValue;
}
}

View File

@ -0,0 +1,19 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TagFloat extends Tag<Float> {
TagFloat(String name, float value) {
super(Type.TAG_Float, name, value);
}
TagFloat(String name, DataInputStream dis) throws IOException {
this(name, dis.readFloat());
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeFloat(value);
}
}

View File

@ -0,0 +1,19 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TagInt extends Tag<Integer> {
TagInt(String name, int value) {
super(Type.TAG_Int, name, value);
}
TagInt(String name, DataInputStream dis) throws IOException {
this(name, dis.readInt());
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeInt(value);
}
}

View File

@ -0,0 +1,35 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class TagIntArray extends Tag<int[]> {
TagIntArray(String name, int[] value) {
super(Type.TAG_Int_Array, name, value);
}
TagIntArray(String name, DataInputStream dis) throws IOException {
this(name, readPayload(dis));
}
private static int[] readPayload(DataInputStream dis) throws IOException {
int length = dis.readInt();
int[] ia = new int[length];
for (int i=0; i<length; i++)
ia[i] = dis.readInt();
return ia;
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeInt(value.length);
for (int v : value)
dos.writeInt(v);
}
@Override
void serialize(PrintStream ps, int indent) {
serializePrefix(ps, indent, ": [", value.length, " integers]");
}
}

View File

@ -0,0 +1,82 @@
package amidst.nbt;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
public class TagList<T> extends SequenceTagBase<T> {
Type listType;
/** Create a new TAG_List NBT tag.
*
* @param name name for the new tag or null to create an unnamed tag.
* @param value list of tags to add to the new tag.
*/
public TagList(String name, Tag<T>[] value) {
super(Type.TAG_List, name, value);
this.listType = value[0].type;
}
/** Create a new TAG_List with an empty list. Use {@link TagList#addTag(Tag)} to add tags later.
*
* @param name name for this tag or null to create an unnamed tag.
* @param listType type of the elements in this empty list.
*/
public TagList(String name, Type listType) {
super(Type.TAG_List, name, new Tag[0]);
this.listType = listType;
}
TagList(String name, DataInputStream dis) throws IOException {
super(Type.TAG_List, name, new Tag[0]);
Object payload = readPayload(dis);
if (payload instanceof Type) {
listType = (Type) payload;
} else {
value = (Tag<T>[]) payload;
listType = value[0].type;
}
}
private static <T> Object readPayload(DataInputStream dis) throws IOException {
Type type = Type.fromByte(dis.readByte());
Tag<T>[] lo = new Tag[dis.readInt()];
for (int i = 0; i < lo.length; i++)
lo[i] = type.readFrom(null, dis);
if (lo.length == 0)
return type;
else
return lo;
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeByte(this.listType.ordinal());
dos.writeInt(value.length);
for (Tag tag : value)
tag.writePayload(dos);
}
/** Add a tag to a TAG_List
*/
public void addTag(Tag tag) {
insertTag(tag, value.length);
}
/** Additional setValue method for empty lists
* @param listType
*/
public void setValue(Type listType) {
value = new Tag[0];
this.listType = listType;
}
@Override
void serialize(PrintStream ps, int indent) {
serializePrefix(ps, indent, ": ", value.length, " entries of type ", listType);
serializeEntries(ps, indent);
}
}

View File

@ -0,0 +1,19 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TagLong extends Tag<Long> {
TagLong(String name, long value) {
super(Type.TAG_Long, name, value);
}
TagLong(String name, DataInputStream dis) throws IOException {
this(name, dis.readLong());
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeLong(value);
}
}

View File

@ -0,0 +1,19 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TagShort extends Tag<Short> {
TagShort(String name, short value) {
super(Type.TAG_Short, name, value);
}
TagShort(String name, DataInputStream dis) throws IOException {
this(name, dis.readShort());
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeShort(value);
}
}

View File

@ -0,0 +1,19 @@
package amidst.nbt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TagString extends Tag<String> {
TagString(String name, String value) {
super(Type.TAG_String, name, value);
}
TagString(String name, DataInputStream dis) throws IOException {
this(name, dis.readUTF());
}
void writePayload(DataOutputStream dos) throws IOException {
dos.writeUTF(value);
}
}

View File

@ -4,6 +4,12 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;
import static amidst.nbt.Tag.Type.*;
/** Tests NBT Tag class.
@ -33,4 +39,40 @@ public class TagTest extends Assert {
assertEquals(TAG_Compound, types[10]);
assertEquals(TAG_Int_Array, types[11]);
}
//testRead covers this implicitly
/* Every Tag subclass has to have a constructor with the interface
* (String, DataInputStream) throws IOException
@Test
public void testConstructors() {
}*/
/** Reads bigtest.nbt from wiki.vg
*/
@Test
public void testRead() throws IOException {
testReadImpl();
}
private TagCompound testReadImpl() throws IOException {
return Tag.readFrom(TagTest.class.getResourceAsStream("bigtest.nbt"));
}
/** Tests correctness of serialization
*/
@Test
public void testSerialization() throws IOException {
TagCompound bigTest = testReadImpl();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
bigTest.serialize(ps, 0);
String serialized = baos.toString();
InputStream reference = TagTest.class.getResourceAsStream("bigtest.nbt.txt");
Scanner s = new Scanner(reference).useDelimiter("\\A");
String expected = s.next();
assertEquals(expected, serialized);
}
}

Binary file not shown.

View File

@ -0,0 +1,45 @@
TAG_Compound("Level"): 11 entries
{
TAG_Long("longTest"): 9223372036854775807
TAG_Short("shortTest"): 32767
TAG_String("stringTest"): HELLO WORLD THIS IS A TEST STRING ÅÄÖ!
TAG_Float("floatTest"): 0.49823147
TAG_Int("intTest"): 2147483647
TAG_Compound("nested compound test"): 2 entries
{
TAG_Compound("ham"): 2 entries
{
TAG_String("name"): Hampus
TAG_Float("value"): 0.75
}
TAG_Compound("egg"): 2 entries
{
TAG_String("name"): Eggbert
TAG_Float("value"): 0.5
}
}
TAG_List("listTest (long)"): 5 entries of type TAG_Long
{
TAG_Long: 11
TAG_Long: 12
TAG_Long: 13
TAG_Long: 14
TAG_Long: 15
}
TAG_List("listTest (compound)"): 2 entries of type TAG_Compound
{
TAG_Compound: 2 entries
{
TAG_String("name"): Compound tag #0
TAG_Long("created-on"): 1264099775885
}
TAG_Compound: 2 entries
{
TAG_String("name"): Compound tag #1
TAG_Long("created-on"): 1264099775885
}
}
TAG_Byte("byteTest"): 127
TAG_Byte_Array("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))"): [1000 bytes]
TAG_Double("doubleTest"): 0.4931287132182315
}