package net.minecraft.nbt; import com.google.common.annotations.VisibleForTesting; import java.io.BufferedOutputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UTFDataFormatException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.Util; import net.minecraft.util.DelegateDataOutput; import net.minecraft.util.FastBufferedInputStream; import org.jetbrains.annotations.Nullable; public class NbtIo { private static final OpenOption[] SYNC_OUTPUT_OPTIONS = new OpenOption[]{ StandardOpenOption.SYNC, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING }; public static CompoundTag readCompressed(Path path, NbtAccounter accounter) throws IOException { InputStream inputStream = Files.newInputStream(path); CompoundTag var4; try { InputStream inputStream2 = new FastBufferedInputStream(inputStream); try { var4 = readCompressed(inputStream2, accounter); } catch (Throwable var8) { try { inputStream2.close(); } catch (Throwable var7) { var8.addSuppressed(var7); } throw var8; } inputStream2.close(); } catch (Throwable var9) { if (inputStream != null) { try { inputStream.close(); } catch (Throwable var6) { var9.addSuppressed(var6); } } throw var9; } if (inputStream != null) { inputStream.close(); } return var4; } private static DataInputStream createDecompressorStream(InputStream zippedStream) throws IOException { return new DataInputStream(new FastBufferedInputStream(new GZIPInputStream(zippedStream))); } private static DataOutputStream createCompressorStream(OutputStream outputSteam) throws IOException { return new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(outputSteam))); } public static CompoundTag readCompressed(InputStream zippedStream, NbtAccounter accounter) throws IOException { DataInputStream dataInputStream = createDecompressorStream(zippedStream); CompoundTag var3; try { var3 = read(dataInputStream, accounter); } catch (Throwable var6) { if (dataInputStream != null) { try { dataInputStream.close(); } catch (Throwable var5) { var6.addSuppressed(var5); } } throw var6; } if (dataInputStream != null) { dataInputStream.close(); } return var3; } public static void parseCompressed(Path path, StreamTagVisitor visitor, NbtAccounter accounter) throws IOException { InputStream inputStream = Files.newInputStream(path); try { InputStream inputStream2 = new FastBufferedInputStream(inputStream); try { parseCompressed(inputStream2, visitor, accounter); } catch (Throwable var9) { try { inputStream2.close(); } catch (Throwable var8) { var9.addSuppressed(var8); } throw var9; } inputStream2.close(); } catch (Throwable var10) { if (inputStream != null) { try { inputStream.close(); } catch (Throwable var7) { var10.addSuppressed(var7); } } throw var10; } if (inputStream != null) { inputStream.close(); } } public static void parseCompressed(InputStream zippedStream, StreamTagVisitor visitor, NbtAccounter accounter) throws IOException { DataInputStream dataInputStream = createDecompressorStream(zippedStream); try { parse(dataInputStream, visitor, accounter); } catch (Throwable var7) { if (dataInputStream != null) { try { dataInputStream.close(); } catch (Throwable var6) { var7.addSuppressed(var6); } } throw var7; } if (dataInputStream != null) { dataInputStream.close(); } } public static void writeCompressed(CompoundTag compoundTag, Path path) throws IOException { OutputStream outputStream = Files.newOutputStream(path, SYNC_OUTPUT_OPTIONS); try { OutputStream outputStream2 = new BufferedOutputStream(outputStream); try { writeCompressed(compoundTag, outputStream2); } catch (Throwable var8) { try { outputStream2.close(); } catch (Throwable var7) { var8.addSuppressed(var7); } throw var8; } outputStream2.close(); } catch (Throwable var9) { if (outputStream != null) { try { outputStream.close(); } catch (Throwable var6) { var9.addSuppressed(var6); } } throw var9; } if (outputStream != null) { outputStream.close(); } } /** * Writes and compresses a compound tag to a GNU zipped file. * @see #writeCompressed(CompoundTag, File) */ public static void writeCompressed(CompoundTag compoundTag, OutputStream outputStream) throws IOException { DataOutputStream dataOutputStream = createCompressorStream(outputStream); try { write(compoundTag, dataOutputStream); } catch (Throwable var6) { if (dataOutputStream != null) { try { dataOutputStream.close(); } catch (Throwable var5) { var6.addSuppressed(var5); } } throw var6; } if (dataOutputStream != null) { dataOutputStream.close(); } } public static void write(CompoundTag compoundTag, Path path) throws IOException { OutputStream outputStream = Files.newOutputStream(path, SYNC_OUTPUT_OPTIONS); try { OutputStream outputStream2 = new BufferedOutputStream(outputStream); try { DataOutputStream dataOutputStream = new DataOutputStream(outputStream2); try { write(compoundTag, dataOutputStream); } catch (Throwable var10) { try { dataOutputStream.close(); } catch (Throwable var9) { var10.addSuppressed(var9); } throw var10; } dataOutputStream.close(); } catch (Throwable var11) { try { outputStream2.close(); } catch (Throwable var8) { var11.addSuppressed(var8); } throw var11; } outputStream2.close(); } catch (Throwable var12) { if (outputStream != null) { try { outputStream.close(); } catch (Throwable var7) { var12.addSuppressed(var7); } } throw var12; } if (outputStream != null) { outputStream.close(); } } @Nullable public static CompoundTag read(Path path) throws IOException { if (!Files.exists(path, new LinkOption[0])) { return null; } else { InputStream inputStream = Files.newInputStream(path); CompoundTag var3; try { DataInputStream dataInputStream = new DataInputStream(inputStream); try { var3 = read(dataInputStream, NbtAccounter.unlimitedHeap()); } catch (Throwable var7) { try { dataInputStream.close(); } catch (Throwable var6) { var7.addSuppressed(var6); } throw var7; } dataInputStream.close(); } catch (Throwable var8) { if (inputStream != null) { try { inputStream.close(); } catch (Throwable var5) { var8.addSuppressed(var5); } } throw var8; } if (inputStream != null) { inputStream.close(); } return var3; } } /** * Reads a compound tag from a file. The size of the file can be infinite. */ public static CompoundTag read(DataInput input) throws IOException { return read(input, NbtAccounter.unlimitedHeap()); } /** * Reads a compound tag from a file. The size of the file is limited by the {@code accounter}. * @throws RuntimeException if the size of the file is larger than the maximum amount of bytes specified by the {@code accounter} */ public static CompoundTag read(DataInput input, NbtAccounter accounter) throws IOException { Tag tag = readUnnamedTag(input, accounter); if (tag instanceof CompoundTag) { return (CompoundTag)tag; } else { throw new IOException("Root tag must be a named compound tag"); } } public static void write(CompoundTag compoundTag, DataOutput output) throws IOException { writeUnnamedTagWithFallback(compoundTag, output); } public static void parse(DataInput input, StreamTagVisitor visitor, NbtAccounter accounter) throws IOException { TagType tagType = TagTypes.getType(input.readByte()); if (tagType == EndTag.TYPE) { if (visitor.visitRootEntry(EndTag.TYPE) == StreamTagVisitor.ValueResult.CONTINUE) { visitor.visitEnd(); } } else { switch (visitor.visitRootEntry(tagType)) { case HALT: default: break; case BREAK: StringTag.skipString(input); tagType.skip(input, accounter); break; case CONTINUE: StringTag.skipString(input); tagType.parse(input, visitor, accounter); } } } public static Tag readAnyTag(DataInput input, NbtAccounter accounter) throws IOException { byte b = input.readByte(); return (Tag)(b == 0 ? EndTag.INSTANCE : readTagSafe(input, accounter, b)); } public static void writeAnyTag(Tag tag, DataOutput output) throws IOException { output.writeByte(tag.getId()); if (tag.getId() != 0) { tag.write(output); } } public static void writeUnnamedTag(Tag tag, DataOutput output) throws IOException { output.writeByte(tag.getId()); if (tag.getId() != 0) { output.writeUTF(""); tag.write(output); } } public static void writeUnnamedTagWithFallback(Tag tag, DataOutput output) throws IOException { writeUnnamedTag(tag, new NbtIo.StringFallbackDataOutput(output)); } @VisibleForTesting public static Tag readUnnamedTag(DataInput input, NbtAccounter accounter) throws IOException { byte b = input.readByte(); if (b == 0) { return EndTag.INSTANCE; } else { StringTag.skipString(input); return readTagSafe(input, accounter, b); } } private static Tag readTagSafe(DataInput input, NbtAccounter accounter, byte type) { try { return TagTypes.getType(type).load(input, accounter); } catch (IOException var6) { CrashReport crashReport = CrashReport.forThrowable(var6, "Loading NBT data"); CrashReportCategory crashReportCategory = crashReport.addCategory("NBT Tag"); crashReportCategory.setDetail("Tag type", type); throw new ReportedNbtException(crashReport); } } public static class StringFallbackDataOutput extends DelegateDataOutput { public StringFallbackDataOutput(DataOutput dataOutput) { super(dataOutput); } @Override public void writeUTF(String string) throws IOException { try { super.writeUTF(string); } catch (UTFDataFormatException var3) { Util.logAndPauseIfInIde("Failed to write NBT String", var3); super.writeUTF(""); } } } }