|
20 | 20 | import org.glavo.nbt.internal.ChunkMetadata; |
21 | 21 | import org.glavo.nbt.internal.ChunkMetadataTable; |
22 | 22 | import org.glavo.nbt.internal.ChunkUtils; |
| 23 | +import org.glavo.nbt.internal.input.DataReader; |
23 | 24 | import org.glavo.nbt.internal.input.InputContext; |
| 25 | +import org.glavo.nbt.internal.input.RawDataReader; |
| 26 | +import org.glavo.nbt.internal.input.ZlibDataReader; |
| 27 | +import org.glavo.nbt.tag.CompoundTag; |
| 28 | +import org.glavo.nbt.tag.Tag; |
24 | 29 |
|
25 | 30 | import java.io.IOException; |
26 | 31 | import java.util.Arrays; |
@@ -57,22 +62,72 @@ static Region readRegion(InputContext context) throws IOException { |
57 | 62 |
|
58 | 63 | List<ChunkMetadata> sortedBySectorOffset = table.getSortedBySectorOffset(); |
59 | 64 |
|
60 | | - long contentStart = context.source.position(); |
| 65 | + var region = new Region(); |
| 66 | + |
| 67 | + long contentStart = context.position(); |
61 | 68 | for (ChunkMetadata chunkMetadata : sortedBySectorOffset) { |
62 | 69 | long sectorStart = contentStart + (long) chunkMetadata.sectorOffset() * ChunkUtils.SECTOR_BYTES; |
| 70 | + long position = context.position(); |
| 71 | + if (position != sectorStart) { |
| 72 | + if (position < sectorStart) { |
| 73 | + context.skip(sectorStart - position); |
| 74 | + } else { |
| 75 | + throw new IOException("Invalid chunk metadata: sector offset points to a position before the current position"); |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + long chunkRawDataLength = context.rawReader.readUnsignedInt(); |
| 80 | + if (chunkRawDataLength < 1) { |
| 81 | + throw new IOException("Invalid chunk data length: " + chunkRawDataLength); |
| 82 | + } |
| 83 | + |
| 84 | + int compressType = context.rawReader.readUnsignedByte(); |
| 85 | + if (compressType > 128) { |
| 86 | + if (chunkRawDataLength != 1) { |
| 87 | + throw new IOException("Invalid chunk data length: %d (expected 1 for compression type %d)".formatted(chunkRawDataLength, compressType)); |
| 88 | + } |
| 89 | + |
| 90 | + throw new IOException("The chunk data is stored externally, and reading this data is not currently supported."); |
| 91 | + } |
| 92 | + |
| 93 | + DataReader reader = switch (compressType) { |
| 94 | + case 1 -> throw new IOException("GZip compression is not supported yet."); |
| 95 | + case 2 -> new ZlibDataReader(context, chunkRawDataLength - 1L); |
| 96 | + case 3 -> new RawDataReader(context, chunkRawDataLength - 1L); |
| 97 | + case 4 -> throw new IOException("LZ4 compression is not supported yet."); |
| 98 | + default -> throw new IOException("Unsupported compression type: " + compressType); |
| 99 | + }; |
63 | 100 |
|
| 101 | + var tag = Tag.readTag(reader); |
| 102 | + if (tag instanceof CompoundTag rootTag) { |
| 103 | + region.getChunk(chunkMetadata.localIndex()).rootTag = rootTag; |
| 104 | + } else { |
| 105 | + throw new IOException("Unexpected tag type: " + tag); |
| 106 | + } |
64 | 107 | } |
65 | 108 |
|
66 | | - // TODO |
67 | | - throw new AssertionError("Not implemented yet"); |
| 109 | + return region; |
68 | 110 | } |
69 | 111 |
|
70 | | - private final Chunk[] chunks = new Chunk[ChunkUtils.CHUNKS_PRE_REGION]; |
| 112 | + private final Chunk[] chunks; |
| 113 | + |
| 114 | + public Region() { |
| 115 | + this.chunks = new Chunk[ChunkUtils.CHUNKS_PRE_REGION]; |
| 116 | + for (int i = 0; i < ChunkUtils.CHUNKS_PRE_REGION; i++) { |
| 117 | + chunks[i] = new Chunk(this, i); |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + public Chunk getChunk(int localIndex) { |
| 122 | + Objects.checkIndex(localIndex, ChunkUtils.CHUNKS_PRE_REGION); |
| 123 | + |
| 124 | + return chunks[localIndex]; |
| 125 | + } |
71 | 126 |
|
72 | 127 | public Chunk getChunk(int x, int z) { |
73 | 128 | Objects.checkIndex(x, ChunkUtils.CHUNKS_PER_REGION_SIDE); |
74 | 129 | Objects.checkIndex(z, ChunkUtils.CHUNKS_PER_REGION_SIDE); |
75 | 130 |
|
76 | | - return chunks[x + z * ChunkUtils.CHUNKS_PER_REGION_SIDE]; |
| 131 | + return chunks[ChunkUtils.toLocalIndex(x, z)]; |
77 | 132 | } |
78 | 133 | } |
0 commit comments