com.sk89q.jnbt.NBTInputStream Maven / Gradle / Ivy
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.sk89q.jnbt;
import com.fastasyncworldedit.core.jnbt.streamer.StreamDelegate;
import com.fastasyncworldedit.core.jnbt.streamer.ValueReader;
import org.enginehub.linbus.stream.LinBinaryIO;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* This class reads NBT, or Named Binary Tag
* streams, and produces an object graph of subclasses of the {@code Tag}
* object.
*
*
* The NBT format was created by Markus Persson, and the specification may be
* found at
* https://minecraft.gamepedia.com/NBT_format.
*
*
* @deprecated JNBT is being removed for lin-bus in WorldEdit 8, use {@link LinBinaryIO} instead
*/
@SuppressWarnings("removal")
@Deprecated(forRemoval = true)
public final class NBTInputStream implements Closeable {
private final DataInputStream is;
/**
* Creates a new {@code NBTInputStream}, which will source its data
* from the specified input stream.
*
* @param is the input stream
*/
public NBTInputStream(InputStream is) {
this.is = new DataInputStream(is);
}
public NBTInputStream(DataInputStream dis) {
this.is = dis;
}
public void mark(int mark) {
is.mark(mark);
}
public void reset() throws IOException {
is.reset();
}
/**
* Reads an NBT tag from the stream.
*
* @return The tag that was read.
*/
public NamedTag readNamedTag() throws IOException {
return readNamedTag(0);
}
/**
* Reads an NBT from the stream.
*
* @param depth the depth of this tag
* @return The tag that was read.
* @throws IOException if an I/O error occurs.
*/
private NamedTag readNamedTag(int depth) throws IOException {
int type = is.readByte() & 0xFF;
return new NamedTag(readNamedTagName(type), readTagPayload(type, depth));
}
public Tag readTag() throws IOException {
int type = is.readByte();
return readTagPayload(type, 0);
}
public void readNamedTagLazy(StreamDelegate scope) throws IOException {
try {
int type = is.readByte();
if (type == NBTConstants.TYPE_END) {
return;
}
StreamDelegate child = scope.get(is);
if (child != null) {
child.acceptRoot(this, type, 0);
} else {
readTagPayloadLazy(type, 0);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
public String readNamedTagName(int type) throws IOException {
if (type != NBTConstants.TYPE_END) {
int nameLength = is.readShort() & 0xFFFF;
byte[] nameBytes = new byte[nameLength];
is.readFully(nameBytes);
return new String(nameBytes, NBTConstants.CHARSET);
} else {
return "";
}
}
private byte[] buf;
public void readTagPayloadLazy(int type, int depth) throws IOException {
switch (type) {
case NBTConstants.TYPE_END:
return;
case NBTConstants.TYPE_BYTE:
is.skipBytes(1);
return;
case NBTConstants.TYPE_SHORT:
is.skipBytes(2);
return;
case NBTConstants.TYPE_INT:
is.skipBytes(4);
return;
case NBTConstants.TYPE_LONG:
is.skipBytes(8);
return;
case NBTConstants.TYPE_FLOAT:
is.skipBytes(4);
return;
case NBTConstants.TYPE_DOUBLE:
is.skipBytes(8);
return;
case NBTConstants.TYPE_STRING:
int length = is.readShort() & 0xFFFF;
is.skipBytes(length);
return;
case NBTConstants.TYPE_BYTE_ARRAY:
is.skipBytes(is.readInt());
return;
case NBTConstants.TYPE_LIST: {
int childType = is.readByte();
length = is.readInt();
for (int i = 0; i < length; ++i) {
readTagPayloadLazy(childType, depth + 1);
}
return;
}
case NBTConstants.TYPE_COMPOUND: {
// readDataPayload
depth++;
while (true) {
int childType = is.readByte();
if (childType == NBTConstants.TYPE_END) {
return;
}
is.skipBytes(is.readShort() & 0xFFFF);
readTagPayloadLazy(childType, depth + 1);
}
}
case NBTConstants.TYPE_INT_ARRAY: {
is.skipBytes(is.readInt() << 2);
return;
}
case NBTConstants.TYPE_LONG_ARRAY: {
is.skipBytes(is.readInt() << 3);
return;
}
default:
throw new IOException("Invalid tag type: " + type + ".");
}
}
public void readTagPayloadLazy(int type, int depth, StreamDelegate scope) throws IOException {
switch (type) {
case NBTConstants.TYPE_END:
return;
case NBTConstants.TYPE_BYTE: {
ValueReader value = scope.getValueReader();
if (value == null) {
value = scope.getElemReader();
}
if (value != null) {
value.applyInt(0, is.readByte());
} else {
is.skipBytes(1);
}
return;
}
case NBTConstants.TYPE_SHORT: {
ValueReader value = scope.getValueReader();
if (value == null) {
value = scope.getElemReader();
}
if (value != null) {
value.applyInt(0, is.readShort());
} else {
is.skipBytes(2);
}
return;
}
case NBTConstants.TYPE_INT: {
ValueReader value = scope.getValueReader();
if (value == null) {
value = scope.getElemReader();
}
if (value != null) {
value.applyInt(0, is.readInt());
} else {
is.skipBytes(4);
}
return;
}
case NBTConstants.TYPE_LONG: {
ValueReader value = scope.getValueReader();
if (value == null) {
value = scope.getElemReader();
}
if (value != null) {
value.applyLong(0, is.readLong());
} else {
is.skipBytes(8);
}
return;
}
case NBTConstants.TYPE_FLOAT: {
ValueReader value = scope.getValueReader();
if (value == null) {
value = scope.getElemReader();
}
if (value != null) {
value.applyFloat(0, is.readFloat());
} else {
is.skipBytes(4);
}
return;
}
case NBTConstants.TYPE_DOUBLE: {
ValueReader value = scope.getValueReader();
if (value == null) {
value = scope.getElemReader();
}
if (value != null) {
value.applyDouble(0, is.readDouble());
} else {
is.skipBytes(8);
}
return;
}
case NBTConstants.TYPE_STRING: {
ValueReader value = scope.getValueReader();
if (value == null) {
value = scope.getElemReader();
}
int length = is.readShort() & 0xFFFF;
if (value != null) {
byte[] bytes = new byte[length];
is.readFully(bytes);
value.apply(0, new String(bytes, NBTConstants.CHARSET));
} else {
is.skipBytes(length);
}
return;
}
case NBTConstants.TYPE_LIST: {
int childType = is.readByte();
int length = is.readInt();
StreamDelegate child;
scope.acceptInfo(length, childType);
ValueReader valueReader = scope.getValueReader();
if (valueReader != null) {
List