--- a/net/minecraft/world/chunk/storage/RegionFile.java +++ b/net/minecraft/world/chunk/storage/RegionFile.java @@ -33,6 +33,7 @@ private final IntBuffer field_76716_d; private final IntBuffer field_227127_h_; private final RegionBitmap field_227128_i_ = new RegionBitmap(); + private final Path filePath; public RegionFile(File p_i225784_1_, File p_i225784_2_) throws IOException { this(p_i225784_1_.toPath(), p_i225784_2_.toPath(), RegionFileVersion.field_227159_b_); @@ -40,6 +41,7 @@ public RegionFile(Path p_i225785_1_, Path p_i225785_2_, RegionFileVersion p_i225785_3_) throws IOException { this.field_227125_e_ = p_i225785_3_; + this.filePath = p_i225785_1_; if (!Files.isDirectory(p_i225785_2_)) { throw new IllegalArgumentException("Expected directory, got " + p_i225785_2_.toAbsolutePath()); } else { @@ -62,6 +64,8 @@ if (k != 0) { int l = func_227142_b_(k); int i1 = func_227131_a_(k); + if (i1 == 255) + i1 = forgeGetRealLength(j, l); this.field_227128_i_.func_227120_a_(l, i1); } } @@ -70,6 +74,66 @@ } } + private int forgeGetRealLength(int index, int offset) throws IOException { + int chunkX = index & 31; + int chunkZ = (index >> 5) & 31; + + ByteBuffer header = ByteBuffer.allocate(5); + this.field_76719_c.read(header, offset * 4096); + ((Buffer)header).flip(); + + if (header.remaining() < 5) { + field_227122_a_.error("Chunk {},{} in {} header is truncated: expected 5 but read {}", chunkX, chunkZ, this.filePath.getFileName(), header.remaining()); + return 255; + } + + return (header.getInt() + 4) / 4096 + 1; + } + + /** + * In 1.14, Forge added support for large chunks by allowing it to overflow the 255 section limit. + * Deferring the section size to the 'length' header in front of the chunk data. + * In 1.15, Mojang solved this issue by adding an external '.mcc' file for large chunks. + * Here, we attempt to detect and extract these large chunks from Forge's format to Vanilla's + */ + public RegionFile extractLargeChunks(ChunkPos pos) throws IOException { + ChunkPos regionBase = new ChunkPos(pos.func_222241_h() * 32, pos.func_222242_i() * 32); + for (int index = 0; index < 1024; index++) { + int offset = this.field_76716_d.get(index); + if (func_227131_a_(offset) != 255) //If it's not 255, then it's not possible to be a oversized chunk. Move on. + continue; + offset = func_227142_b_(offset); + + ChunkPos chunk = new ChunkPos(regionBase.field_77276_a + (index & 31), regionBase.field_77275_b + ((index >> 5) & 31)); + + ByteBuffer header = ByteBuffer.allocate(5); + this.field_76719_c.read(header, offset * 4096); + ((Buffer)header).flip(); + + if (header.remaining() < 5) { + field_227122_a_.error("Chunk {} in {} header is truncated: expected 5 but read {}", chunk, this.filePath.getFileName(), header.remaining()); + continue; + } + + int length = header.getInt(); + byte version = header.get(); + int sectors = (length + 4) / 4096 + 1; + if (sectors <= 255 || func_227130_a_(version)) + continue; //Not over sized, or already external + + ByteBuffer data = ByteBuffer.allocate(length + 4); + this.field_76719_c.read(data, offset * 4096); + ((Buffer)data).flip(); + + if (data.remaining() < length + 4) { + field_227122_a_.error("Chunk {} in {} is truncated: expected {} but read {}", chunk, this.filePath.getFileName(), length + 4, data.remaining()); + continue; + } + func_227135_a_(chunk, data); //Save the chunk data, it'll be spit out to an external file. + } + return this; + } + private Path func_227145_e_(ChunkPos p_227145_1_) { String s = "c." + p_227145_1_.field_77276_a + "." + p_227145_1_.field_77275_b + ".mcc"; return this.field_227124_d_.resolve(s); @@ -225,6 +289,7 @@ int j = this.field_76716_d.get(i); int k = func_227142_b_(j); int l = func_227131_a_(j); + if (l == 255) l = forgeGetRealLength(i, k); //Forge: Old Forge fix, get real length, so we can free if needed int i1 = p_227135_2_.remaining(); int j1 = func_227144_c_(i1); int k1;