package net.minecraft.world.chunk.storage; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.InflaterInputStream; public class RegionFile { private static final byte[] emptySector = new byte[4096]; private final File fileName; private RandomAccessFile dataFile; private final int[] offsets = new int[1024]; private final int[] chunkTimestamps = new int[1024]; private ArrayList sectorFree; /** McRegion sizeDelta */ private int sizeDelta; private long lastModified = 0L; public RegionFile(File par1File) { this.fileName = par1File; this.sizeDelta = 0; try { if (par1File.exists()) { this.lastModified = par1File.lastModified(); } this.dataFile = new RandomAccessFile(par1File, "rw"); int i; if (this.dataFile.length() < 4096L) { for (i = 0; i < 1024; ++i) { this.dataFile.writeInt(0); } for (i = 0; i < 1024; ++i) { this.dataFile.writeInt(0); } this.sizeDelta += 8192; } if ((this.dataFile.length() & 4095L) != 0L) { for (i = 0; (long)i < (this.dataFile.length() & 4095L); ++i) { this.dataFile.write(0); } } i = (int)this.dataFile.length() / 4096; this.sectorFree = new ArrayList(i); int j; for (j = 0; j < i; ++j) { this.sectorFree.add(Boolean.valueOf(true)); } this.sectorFree.set(0, Boolean.valueOf(false)); this.sectorFree.set(1, Boolean.valueOf(false)); this.dataFile.seek(0L); int k; for (j = 0; j < 1024; ++j) { k = this.dataFile.readInt(); this.offsets[j] = k; if (k != 0 && (k >> 8) + (k & 255) <= this.sectorFree.size()) { for (int l = 0; l < (k & 255); ++l) { this.sectorFree.set((k >> 8) + l, Boolean.valueOf(false)); } } } for (j = 0; j < 1024; ++j) { k = this.dataFile.readInt(); this.chunkTimestamps[j] = k; } } catch (IOException ioexception) { ioexception.printStackTrace(); } } /** * args: x, y - get uncompressed chunk stream from the region file */ public synchronized DataInputStream getChunkDataInputStream(int par1, int par2) { if (this.outOfBounds(par1, par2)) { return null; } else { try { int k = this.getOffset(par1, par2); if (k == 0) { return null; } else { int l = k >> 8; int i1 = k & 255; if (l + i1 > this.sectorFree.size()) { return null; } else { this.dataFile.seek((long)(l * 4096)); int j1 = this.dataFile.readInt(); if (j1 > 4096 * i1) { return null; } else if (j1 <= 0) { return null; } else { byte b0 = this.dataFile.readByte(); byte[] abyte; if (b0 == 1) { abyte = new byte[j1 - 1]; this.dataFile.read(abyte); return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(abyte)))); } else if (b0 == 2) { abyte = new byte[j1 - 1]; this.dataFile.read(abyte); return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(abyte)))); } else { return null; } } } } } catch (IOException ioexception) { return null; } } } /** * args: x, z - get an output stream used to write chunk data, data is on disk when the returned stream is closed */ public DataOutputStream getChunkDataOutputStream(int par1, int par2) { return this.outOfBounds(par1, par2) ? null : new DataOutputStream(new DeflaterOutputStream(new RegionFileChunkBuffer(this, par1, par2))); } /** * args: x, z, data, length - write chunk data at (x, z) to disk */ protected synchronized void write(int par1, int par2, byte[] par3ArrayOfByte, int par4) { try { int l = this.getOffset(par1, par2); int i1 = l >> 8; int j1 = l & 255; int k1 = (par4 + 5) / 4096 + 1; if (k1 >= 256) { return; } if (i1 != 0 && j1 == k1) { this.write(i1, par3ArrayOfByte, par4); } else { int l1; for (l1 = 0; l1 < j1; ++l1) { this.sectorFree.set(i1 + l1, Boolean.valueOf(true)); } l1 = this.sectorFree.indexOf(Boolean.valueOf(true)); int i2 = 0; int j2; if (l1 != -1) { for (j2 = l1; j2 < this.sectorFree.size(); ++j2) { if (i2 != 0) { if (((Boolean)this.sectorFree.get(j2)).booleanValue()) { ++i2; } else { i2 = 0; } } else if (((Boolean)this.sectorFree.get(j2)).booleanValue()) { l1 = j2; i2 = 1; } if (i2 >= k1) { break; } } } if (i2 >= k1) { i1 = l1; this.setOffset(par1, par2, l1 << 8 | k1); for (j2 = 0; j2 < k1; ++j2) { this.sectorFree.set(i1 + j2, Boolean.valueOf(false)); } this.write(i1, par3ArrayOfByte, par4); } else { this.dataFile.seek(this.dataFile.length()); i1 = this.sectorFree.size(); for (j2 = 0; j2 < k1; ++j2) { this.dataFile.write(emptySector); this.sectorFree.add(Boolean.valueOf(false)); } this.sizeDelta += 4096 * k1; this.write(i1, par3ArrayOfByte, par4); this.setOffset(par1, par2, i1 << 8 | k1); } } this.setChunkTimestamp(par1, par2, (int)(System.currentTimeMillis() / 1000L)); } catch (IOException ioexception) { ioexception.printStackTrace(); } } /** * args: sectorNumber, data, length - write the chunk data to this RegionFile */ private void write(int par1, byte[] par2ArrayOfByte, int par3) throws IOException { this.dataFile.seek((long)(par1 * 4096)); this.dataFile.writeInt(par3 + 1); this.dataFile.writeByte(2); this.dataFile.write(par2ArrayOfByte, 0, par3); } /** * args: x, z - check region bounds */ private boolean outOfBounds(int par1, int par2) { return par1 < 0 || par1 >= 32 || par2 < 0 || par2 >= 32; } /** * args: x, y - get chunk's offset in region file */ private int getOffset(int par1, int par2) { return this.offsets[par1 + par2 * 32]; } /** * args: x, z, - true if chunk has been saved / converted */ public boolean isChunkSaved(int par1, int par2) { return this.getOffset(par1, par2) != 0; } /** * args: x, z, offset - sets the chunk's offset in the region file */ private void setOffset(int par1, int par2, int par3) throws IOException { this.offsets[par1 + par2 * 32] = par3; this.dataFile.seek((long)((par1 + par2 * 32) * 4)); this.dataFile.writeInt(par3); } /** * args: x, z, timestamp - sets the chunk's write timestamp */ private void setChunkTimestamp(int par1, int par2, int par3) throws IOException { this.chunkTimestamps[par1 + par2 * 32] = par3; this.dataFile.seek((long)(4096 + (par1 + par2 * 32) * 4)); this.dataFile.writeInt(par3); } /** * close this RegionFile and prevent further writes */ public void close() throws IOException { if (this.dataFile != null) { this.dataFile.close(); } } }