001package net.minecraft.world.chunk.storage; 002 003import java.io.BufferedInputStream; 004import java.io.ByteArrayInputStream; 005import java.io.DataInputStream; 006import java.io.DataOutputStream; 007import java.io.File; 008import java.io.IOException; 009import java.io.RandomAccessFile; 010import java.util.ArrayList; 011import java.util.zip.DeflaterOutputStream; 012import java.util.zip.GZIPInputStream; 013import java.util.zip.InflaterInputStream; 014 015public class RegionFile 016{ 017 private static final byte[] emptySector = new byte[4096]; 018 private final File fileName; 019 private RandomAccessFile dataFile; 020 private final int[] offsets = new int[1024]; 021 private final int[] chunkTimestamps = new int[1024]; 022 private ArrayList sectorFree; 023 024 /** McRegion sizeDelta */ 025 private int sizeDelta; 026 private long lastModified = 0L; 027 028 public RegionFile(File par1File) 029 { 030 this.fileName = par1File; 031 this.sizeDelta = 0; 032 033 try 034 { 035 if (par1File.exists()) 036 { 037 this.lastModified = par1File.lastModified(); 038 } 039 040 this.dataFile = new RandomAccessFile(par1File, "rw"); 041 int i; 042 043 if (this.dataFile.length() < 4096L) 044 { 045 for (i = 0; i < 1024; ++i) 046 { 047 this.dataFile.writeInt(0); 048 } 049 050 for (i = 0; i < 1024; ++i) 051 { 052 this.dataFile.writeInt(0); 053 } 054 055 this.sizeDelta += 8192; 056 } 057 058 if ((this.dataFile.length() & 4095L) != 0L) 059 { 060 for (i = 0; (long)i < (this.dataFile.length() & 4095L); ++i) 061 { 062 this.dataFile.write(0); 063 } 064 } 065 066 i = (int)this.dataFile.length() / 4096; 067 this.sectorFree = new ArrayList(i); 068 int j; 069 070 for (j = 0; j < i; ++j) 071 { 072 this.sectorFree.add(Boolean.valueOf(true)); 073 } 074 075 this.sectorFree.set(0, Boolean.valueOf(false)); 076 this.sectorFree.set(1, Boolean.valueOf(false)); 077 this.dataFile.seek(0L); 078 int k; 079 080 for (j = 0; j < 1024; ++j) 081 { 082 k = this.dataFile.readInt(); 083 this.offsets[j] = k; 084 085 if (k != 0 && (k >> 8) + (k & 255) <= this.sectorFree.size()) 086 { 087 for (int l = 0; l < (k & 255); ++l) 088 { 089 this.sectorFree.set((k >> 8) + l, Boolean.valueOf(false)); 090 } 091 } 092 } 093 094 for (j = 0; j < 1024; ++j) 095 { 096 k = this.dataFile.readInt(); 097 this.chunkTimestamps[j] = k; 098 } 099 } 100 catch (IOException ioexception) 101 { 102 ioexception.printStackTrace(); 103 } 104 } 105 106 /** 107 * args: x, y - get uncompressed chunk stream from the region file 108 */ 109 public synchronized DataInputStream getChunkDataInputStream(int par1, int par2) 110 { 111 if (this.outOfBounds(par1, par2)) 112 { 113 return null; 114 } 115 else 116 { 117 try 118 { 119 int k = this.getOffset(par1, par2); 120 121 if (k == 0) 122 { 123 return null; 124 } 125 else 126 { 127 int l = k >> 8; 128 int i1 = k & 255; 129 130 if (l + i1 > this.sectorFree.size()) 131 { 132 return null; 133 } 134 else 135 { 136 this.dataFile.seek((long)(l * 4096)); 137 int j1 = this.dataFile.readInt(); 138 139 if (j1 > 4096 * i1) 140 { 141 return null; 142 } 143 else if (j1 <= 0) 144 { 145 return null; 146 } 147 else 148 { 149 byte b0 = this.dataFile.readByte(); 150 byte[] abyte; 151 152 if (b0 == 1) 153 { 154 abyte = new byte[j1 - 1]; 155 this.dataFile.read(abyte); 156 return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(abyte)))); 157 } 158 else if (b0 == 2) 159 { 160 abyte = new byte[j1 - 1]; 161 this.dataFile.read(abyte); 162 return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(abyte)))); 163 } 164 else 165 { 166 return null; 167 } 168 } 169 } 170 } 171 } 172 catch (IOException ioexception) 173 { 174 return null; 175 } 176 } 177 } 178 179 /** 180 * args: x, z - get an output stream used to write chunk data, data is on disk when the returned stream is closed 181 */ 182 public DataOutputStream getChunkDataOutputStream(int par1, int par2) 183 { 184 return this.outOfBounds(par1, par2) ? null : new DataOutputStream(new DeflaterOutputStream(new RegionFileChunkBuffer(this, par1, par2))); 185 } 186 187 /** 188 * args: x, z, data, length - write chunk data at (x, z) to disk 189 */ 190 protected synchronized void write(int par1, int par2, byte[] par3ArrayOfByte, int par4) 191 { 192 try 193 { 194 int l = this.getOffset(par1, par2); 195 int i1 = l >> 8; 196 int j1 = l & 255; 197 int k1 = (par4 + 5) / 4096 + 1; 198 199 if (k1 >= 256) 200 { 201 return; 202 } 203 204 if (i1 != 0 && j1 == k1) 205 { 206 this.write(i1, par3ArrayOfByte, par4); 207 } 208 else 209 { 210 int l1; 211 212 for (l1 = 0; l1 < j1; ++l1) 213 { 214 this.sectorFree.set(i1 + l1, Boolean.valueOf(true)); 215 } 216 217 l1 = this.sectorFree.indexOf(Boolean.valueOf(true)); 218 int i2 = 0; 219 int j2; 220 221 if (l1 != -1) 222 { 223 for (j2 = l1; j2 < this.sectorFree.size(); ++j2) 224 { 225 if (i2 != 0) 226 { 227 if (((Boolean)this.sectorFree.get(j2)).booleanValue()) 228 { 229 ++i2; 230 } 231 else 232 { 233 i2 = 0; 234 } 235 } 236 else if (((Boolean)this.sectorFree.get(j2)).booleanValue()) 237 { 238 l1 = j2; 239 i2 = 1; 240 } 241 242 if (i2 >= k1) 243 { 244 break; 245 } 246 } 247 } 248 249 if (i2 >= k1) 250 { 251 i1 = l1; 252 this.setOffset(par1, par2, l1 << 8 | k1); 253 254 for (j2 = 0; j2 < k1; ++j2) 255 { 256 this.sectorFree.set(i1 + j2, Boolean.valueOf(false)); 257 } 258 259 this.write(i1, par3ArrayOfByte, par4); 260 } 261 else 262 { 263 this.dataFile.seek(this.dataFile.length()); 264 i1 = this.sectorFree.size(); 265 266 for (j2 = 0; j2 < k1; ++j2) 267 { 268 this.dataFile.write(emptySector); 269 this.sectorFree.add(Boolean.valueOf(false)); 270 } 271 272 this.sizeDelta += 4096 * k1; 273 this.write(i1, par3ArrayOfByte, par4); 274 this.setOffset(par1, par2, i1 << 8 | k1); 275 } 276 } 277 278 this.setChunkTimestamp(par1, par2, (int)(System.currentTimeMillis() / 1000L)); 279 } 280 catch (IOException ioexception) 281 { 282 ioexception.printStackTrace(); 283 } 284 } 285 286 /** 287 * args: sectorNumber, data, length - write the chunk data to this RegionFile 288 */ 289 private void write(int par1, byte[] par2ArrayOfByte, int par3) throws IOException 290 { 291 this.dataFile.seek((long)(par1 * 4096)); 292 this.dataFile.writeInt(par3 + 1); 293 this.dataFile.writeByte(2); 294 this.dataFile.write(par2ArrayOfByte, 0, par3); 295 } 296 297 /** 298 * args: x, z - check region bounds 299 */ 300 private boolean outOfBounds(int par1, int par2) 301 { 302 return par1 < 0 || par1 >= 32 || par2 < 0 || par2 >= 32; 303 } 304 305 /** 306 * args: x, y - get chunk's offset in region file 307 */ 308 private int getOffset(int par1, int par2) 309 { 310 return this.offsets[par1 + par2 * 32]; 311 } 312 313 /** 314 * args: x, z, - true if chunk has been saved / converted 315 */ 316 public boolean isChunkSaved(int par1, int par2) 317 { 318 return this.getOffset(par1, par2) != 0; 319 } 320 321 /** 322 * args: x, z, offset - sets the chunk's offset in the region file 323 */ 324 private void setOffset(int par1, int par2, int par3) throws IOException 325 { 326 this.offsets[par1 + par2 * 32] = par3; 327 this.dataFile.seek((long)((par1 + par2 * 32) * 4)); 328 this.dataFile.writeInt(par3); 329 } 330 331 /** 332 * args: x, z, timestamp - sets the chunk's write timestamp 333 */ 334 private void setChunkTimestamp(int par1, int par2, int par3) throws IOException 335 { 336 this.chunkTimestamps[par1 + par2 * 32] = par3; 337 this.dataFile.seek((long)(4096 + (par1 + par2 * 32) * 4)); 338 this.dataFile.writeInt(par3); 339 } 340 341 /** 342 * close this RegionFile and prevent further writes 343 */ 344 public void close() throws IOException 345 { 346 if (this.dataFile != null) 347 { 348 this.dataFile.close(); 349 } 350 } 351}