425 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| 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("");
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |