blob: 7c7d8ed256950f7b941f9935bfae92be0af0a7e6 [file] [log] [blame] [raw]
package net.querz.nbt.io;
import net.querz.io.ExceptionBiFunction;
import net.querz.io.MaxDepthIO;
import net.querz.nbt.tag.ByteArrayTag;
import net.querz.nbt.tag.ByteTag;
import net.querz.nbt.tag.CompoundTag;
import net.querz.nbt.tag.DoubleTag;
import net.querz.nbt.tag.EndTag;
import net.querz.nbt.tag.FloatTag;
import net.querz.nbt.tag.IntArrayTag;
import net.querz.nbt.tag.IntTag;
import net.querz.nbt.tag.ListTag;
import net.querz.nbt.tag.LongArrayTag;
import net.querz.nbt.tag.LongTag;
import net.querz.nbt.tag.ShortTag;
import net.querz.nbt.tag.StringTag;
import net.querz.nbt.tag.Tag;
import java.io.Closeable;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class LittleEndianNBTInputStream implements DataInput, NBTInput, MaxDepthIO, Closeable {
private final DataInputStream input;
private static Map<Byte, ExceptionBiFunction<LittleEndianNBTInputStream, Integer, ? extends Tag<?>, IOException>> readers = new HashMap<>();
private static Map<Byte, Class<?>> idClassMapping = new HashMap<>();
static {
put(EndTag.ID, (i, d) -> EndTag.INSTANCE, EndTag.class);
put(ByteTag.ID, (i, d) -> readByte(i), ByteTag.class);
put(ShortTag.ID, (i, d) -> readShort(i), ShortTag.class);
put(IntTag.ID, (i, d) -> readInt(i), IntTag.class);
put(LongTag.ID, (i, d) -> readLong(i), LongTag.class);
put(FloatTag.ID, (i, d) -> readFloat(i), FloatTag.class);
put(DoubleTag.ID, (i, d) -> readDouble(i), DoubleTag.class);
put(ByteArrayTag.ID, (i, d) -> readByteArray(i), ByteArrayTag.class);
put(StringTag.ID, (i, d) -> readString(i), StringTag.class);
put(ListTag.ID, LittleEndianNBTInputStream::readListTag, ListTag.class);
put(CompoundTag.ID, LittleEndianNBTInputStream::readCompound, CompoundTag.class);
put(IntArrayTag.ID, (i, d) -> readIntArray(i), IntArrayTag.class);
put(LongArrayTag.ID, (i, d) -> readLongArray(i), LongArrayTag.class);
}
private static void put(byte id, ExceptionBiFunction<LittleEndianNBTInputStream, Integer, ? extends Tag<?>, IOException> reader, Class<?> clazz) {
readers.put(id, reader);
idClassMapping.put(id, clazz);
}
public LittleEndianNBTInputStream(InputStream in) {
input = new DataInputStream(in);
}
public LittleEndianNBTInputStream(DataInputStream in) {
input = in;
}
public NamedTag readTag(int maxDepth) throws IOException {
byte id = readByte();
return new NamedTag(readUTF(), readTag(id, maxDepth));
}
public Tag<?> readRawTag(int maxDepth) throws IOException {
byte id = readByte();
return readTag(id, maxDepth);
}
private Tag<?> readTag(byte type, int maxDepth) throws IOException {
ExceptionBiFunction<LittleEndianNBTInputStream, Integer, ? extends Tag<?>, IOException> f;
if ((f = readers.get(type)) == null) {
throw new IOException("invalid tag id \"" + type + "\"");
}
return f.accept(this, maxDepth);
}
private static ByteTag readByte(LittleEndianNBTInputStream in) throws IOException {
return new ByteTag(in.readByte());
}
private static ShortTag readShort(LittleEndianNBTInputStream in) throws IOException {
return new ShortTag(in.readShort());
}
private static IntTag readInt(LittleEndianNBTInputStream in) throws IOException {
return new IntTag(in.readInt());
}
private static LongTag readLong(LittleEndianNBTInputStream in) throws IOException {
return new LongTag(in.readLong());
}
private static FloatTag readFloat(LittleEndianNBTInputStream in) throws IOException {
return new FloatTag(in.readFloat());
}
private static DoubleTag readDouble(LittleEndianNBTInputStream in) throws IOException {
return new DoubleTag(in.readDouble());
}
private static StringTag readString(LittleEndianNBTInputStream in) throws IOException {
return new StringTag(in.readUTF());
}
private static ByteArrayTag readByteArray(LittleEndianNBTInputStream in) throws IOException {
ByteArrayTag bat = new ByteArrayTag(new byte[in.readInt()]);
in.readFully(bat.getValue());
return bat;
}
private static IntArrayTag readIntArray(LittleEndianNBTInputStream in) throws IOException {
int l = in.readInt();
int[] data = new int[l];
IntArrayTag iat = new IntArrayTag(data);
for (int i = 0; i < l; i++) {
data[i] = in.readInt();
}
return iat;
}
private static LongArrayTag readLongArray(LittleEndianNBTInputStream in) throws IOException {
int l = in.readInt();
long[] data = new long[l];
LongArrayTag iat = new LongArrayTag(data);
for (int i = 0; i < l; i++) {
data[i] = in.readLong();
}
return iat;
}
private static ListTag<?> readListTag(LittleEndianNBTInputStream in, int maxDepth) throws IOException {
byte listType = in.readByte();
ListTag<?> list = ListTag.createUnchecked(idClassMapping.get(listType));
int length = in.readInt();
if (length < 0) {
length = 0;
}
for (int i = 0; i < length; i++) {
list.addUnchecked(in.readTag(listType, in.decrementMaxDepth(maxDepth)));
}
return list;
}
private static CompoundTag readCompound(LittleEndianNBTInputStream in, int maxDepth) throws IOException {
CompoundTag comp = new CompoundTag();
for (int id = in.readByte() & 0xFF; id != 0; id = in.readByte() & 0xFF) {
String key = in.readUTF();
Tag<?> element = in.readTag((byte) id, in.decrementMaxDepth(maxDepth));
comp.put(key, element);
}
return comp;
}
@Override
public void readFully(byte[] b) throws IOException {
input.readFully(b);
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
input.readFully(b, off, len);
}
@Override
public int skipBytes(int n) throws IOException {
return input.skipBytes(n);
}
@Override
public boolean readBoolean() throws IOException {
return input.readBoolean();
}
@Override
public byte readByte() throws IOException {
return input.readByte();
}
@Override
public int readUnsignedByte() throws IOException {
return input.readUnsignedByte();
}
@Override
public short readShort() throws IOException {
return Short.reverseBytes(input.readShort());
}
public int readUnsignedShort() throws IOException {
return Short.toUnsignedInt(Short.reverseBytes(input.readShort()));
}
@Override
public char readChar() throws IOException {
return Character.reverseBytes(input.readChar());
}
@Override
public int readInt() throws IOException {
return Integer.reverseBytes(input.readInt());
}
@Override
public long readLong() throws IOException {
return Long.reverseBytes(input.readLong());
}
@Override
public float readFloat() throws IOException {
return Float.intBitsToFloat(Integer.reverseBytes(input.readInt()));
}
@Override
public double readDouble() throws IOException {
return Double.longBitsToDouble(Long.reverseBytes(input.readLong()));
}
@Override
@Deprecated
public String readLine() throws IOException {
return input.readLine();
}
@Override
public void close() throws IOException {
input.close();
}
@Override
public String readUTF() throws IOException {
byte[] bytes = new byte[readUnsignedShort()];
readFully(bytes);
return new String(bytes, StandardCharsets.UTF_8);
}
}