From b51d41c0acb0e3bb7f46bb31e69086c9c759bbf5 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sun, 28 Aug 2016 16:44:54 +1000 Subject: [PATCH] NBT streams --- core/src/main/java/com/boydti/fawe/Fawe.java | 5 + .../java/com/sk89q/jnbt/NBTInputStream.java | 8 + .../java/com/sk89q/jnbt/NBTOutputStream.java | 304 ++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index 291f1f90..74bd9216 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -16,6 +16,8 @@ import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.WEManager; import com.boydti.fawe.util.WESubscriber; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; @@ -366,6 +368,9 @@ public class Fawe { // BlockData BlockData.inject(); // Temporary fix for 1.9.4 BundledBlockData.inject(); // Add custom rotation + // NBT + NBTInputStream.inject(); // Add actual streaming + Optimizations + New methods + NBTOutputStream.inject(); // New methods try { CommandManager.inject(); // Async commands PlatformManager.inject(); // Async brushes / tools diff --git a/core/src/main/java/com/sk89q/jnbt/NBTInputStream.java b/core/src/main/java/com/sk89q/jnbt/NBTInputStream.java index 9b028c73..69c3c20c 100644 --- a/core/src/main/java/com/sk89q/jnbt/NBTInputStream.java +++ b/core/src/main/java/com/sk89q/jnbt/NBTInputStream.java @@ -81,6 +81,11 @@ public final class NBTInputStream implements Closeable { return new NamedTag(readNamedTagName(type), readTagPayload(type, depth)); } + public Tag readTag() throws IOException { + int type = is.readByte(); + return readTagPayload(type, 0); + } + public void readNamedTagLazy(RunnableVal2 getReader) throws IOException { int type = is.readByte(); String name = readNamedTagName(type); @@ -397,4 +402,7 @@ public final class NBTInputStream implements Closeable { } } + public static Class inject() { + return NBTInputStream.class; + } } diff --git a/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java b/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java new file mode 100644 index 00000000..6d94b718 --- /dev/null +++ b/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java @@ -0,0 +1,304 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.jnbt; + +import java.io.Closeable; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; + + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * This class writes NBT, or Named Binary Tag + * {@code Tag} objects to an underlying {@code OutputStream}. + * + *

The NBT format was created by Markus Persson, and the specification may be + * found at + * http://www.minecraft.net/docs/NBT.txt.

+ */ +public final class NBTOutputStream implements Closeable { + + /** + * The output stream. + */ + private final DataOutputStream os; + + /** + * Creates a new {@code NBTOutputStream}, which will write data to the + * specified underlying output stream. + * + * @param os + * The output stream. + * @throws IOException + * if an I/O error occurs. + */ + public NBTOutputStream(OutputStream os) throws IOException { + this.os = new DataOutputStream(os); + } + + /** + * Writes a tag. + * + * @param tag + * The tag to write. + * @throws IOException + * if an I/O error occurs. + */ + public void writeNamedTag(String name, Tag tag) throws IOException { + checkNotNull(name); + checkNotNull(tag); + + int type = NBTUtils.getTypeCode(tag.getClass()); + byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); + + os.writeByte(type); + os.writeShort(nameBytes.length); + os.write(nameBytes); + + if (type == NBTConstants.TYPE_END) { + throw new IOException("Named TAG_End not permitted."); + } + + writeTagPayload(tag); + } + + public void writeTag(Tag tag) throws IOException { + int type = NBTUtils.getTypeCode(tag.getClass()); + os.writeByte(type); + writeTagPayload(tag); + } + + /** + * Writes tag payload. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeTagPayload(Tag tag) throws IOException { + int type = NBTUtils.getTypeCode(tag.getClass()); + switch (type) { + case NBTConstants.TYPE_END: + writeEndTagPayload((EndTag) tag); + break; + case NBTConstants.TYPE_BYTE: + writeByteTagPayload((ByteTag) tag); + break; + case NBTConstants.TYPE_SHORT: + writeShortTagPayload((ShortTag) tag); + break; + case NBTConstants.TYPE_INT: + writeIntTagPayload((IntTag) tag); + break; + case NBTConstants.TYPE_LONG: + writeLongTagPayload((LongTag) tag); + break; + case NBTConstants.TYPE_FLOAT: + writeFloatTagPayload((FloatTag) tag); + break; + case NBTConstants.TYPE_DOUBLE: + writeDoubleTagPayload((DoubleTag) tag); + break; + case NBTConstants.TYPE_BYTE_ARRAY: + writeByteArrayTagPayload((ByteArrayTag) tag); + break; + case NBTConstants.TYPE_STRING: + writeStringTagPayload((StringTag) tag); + break; + case NBTConstants.TYPE_LIST: + writeListTagPayload((ListTag) tag); + break; + case NBTConstants.TYPE_COMPOUND: + writeCompoundTagPayload((CompoundTag) tag); + break; + case NBTConstants.TYPE_INT_ARRAY: + writeIntArrayTagPayload((IntArrayTag) tag); + break; + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + /** + * Writes a {@code TAG_Byte} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeByteTagPayload(ByteTag tag) throws IOException { + os.writeByte(tag.getValue()); + } + + /** + * Writes a {@code TAG_Byte_Array} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeByteArrayTagPayload(ByteArrayTag tag) throws IOException { + byte[] bytes = tag.getValue(); + os.writeInt(bytes.length); + os.write(bytes); + } + + /** + * Writes a {@code TAG_Compound} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeCompoundTagPayload(CompoundTag tag) throws IOException { + for (Map.Entry entry : tag.getValue().entrySet()) { + writeNamedTag(entry.getKey(), entry.getValue()); + } + os.writeByte((byte) 0); // end tag - better way? + } + + /** + * Writes a {@code TAG_List} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeListTagPayload(ListTag tag) throws IOException { + Class clazz = tag.getType(); + List tags = tag.getValue(); + int size = tags.size(); + + os.writeByte(NBTUtils.getTypeCode(clazz)); + os.writeInt(size); + for (Tag tag1 : tags) { + writeTagPayload(tag1); + } + } + + /** + * Writes a {@code TAG_String} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeStringTagPayload(StringTag tag) throws IOException { + byte[] bytes = tag.getValue().getBytes(NBTConstants.CHARSET); + os.writeShort(bytes.length); + os.write(bytes); + } + + /** + * Writes a {@code TAG_Double} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeDoubleTagPayload(DoubleTag tag) throws IOException { + os.writeDouble(tag.getValue()); + } + + /** + * Writes a {@code TAG_Float} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeFloatTagPayload(FloatTag tag) throws IOException { + os.writeFloat(tag.getValue()); + } + + /** + * Writes a {@code TAG_Long} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeLongTagPayload(LongTag tag) throws IOException { + os.writeLong(tag.getValue()); + } + + /** + * Writes a {@code TAG_Int} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeIntTagPayload(IntTag tag) throws IOException { + os.writeInt(tag.getValue()); + } + + /** + * Writes a {@code TAG_Short} tag. + * + * @param tag + * The tag. + * @throws IOException + * if an I/O error occurs. + */ + private void writeShortTagPayload(ShortTag tag) throws IOException { + os.writeShort(tag.getValue()); + } + + /** + * Writes a {@code TAG_Empty} tag. + * + * @param tag the tag + */ + private void writeEndTagPayload(EndTag tag) { + /* empty */ + } + + private void writeIntArrayTagPayload(IntArrayTag tag) throws IOException { + int[] data = tag.getValue(); + os.writeInt(data.length); + for (int aData : data) { + os.writeInt(aData); + } + } + + @Override + public void close() throws IOException { + os.close(); + } + + public static Class inject() { + return NBTOutputStream.class; + } +} \ No newline at end of file