001 package net.minecraft.world.chunk.storage; 002 003 import java.io.BufferedInputStream; 004 import java.io.ByteArrayInputStream; 005 import java.io.DataInputStream; 006 import java.io.DataOutputStream; 007 import java.io.File; 008 import java.io.IOException; 009 import java.io.RandomAccessFile; 010 import java.util.ArrayList; 011 import java.util.zip.DeflaterOutputStream; 012 import java.util.zip.GZIPInputStream; 013 import java.util.zip.InflaterInputStream; 014 015 public 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 var2; 042 043 if (this.dataFile.length() < 4096L) 044 { 045 for (var2 = 0; var2 < 1024; ++var2) 046 { 047 this.dataFile.writeInt(0); 048 } 049 050 for (var2 = 0; var2 < 1024; ++var2) 051 { 052 this.dataFile.writeInt(0); 053 } 054 055 this.sizeDelta += 8192; 056 } 057 058 if ((this.dataFile.length() & 4095L) != 0L) 059 { 060 for (var2 = 0; (long)var2 < (this.dataFile.length() & 4095L); ++var2) 061 { 062 this.dataFile.write(0); 063 } 064 } 065 066 var2 = (int)this.dataFile.length() / 4096; 067 this.sectorFree = new ArrayList(var2); 068 int var3; 069 070 for (var3 = 0; var3 < var2; ++var3) 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 var4; 079 080 for (var3 = 0; var3 < 1024; ++var3) 081 { 082 var4 = this.dataFile.readInt(); 083 this.offsets[var3] = var4; 084 085 if (var4 != 0 && (var4 >> 8) + (var4 & 255) <= this.sectorFree.size()) 086 { 087 for (int var5 = 0; var5 < (var4 & 255); ++var5) 088 { 089 this.sectorFree.set((var4 >> 8) + var5, Boolean.valueOf(false)); 090 } 091 } 092 } 093 094 for (var3 = 0; var3 < 1024; ++var3) 095 { 096 var4 = this.dataFile.readInt(); 097 this.chunkTimestamps[var3] = var4; 098 } 099 } 100 catch (IOException var6) 101 { 102 var6.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 var3 = this.getOffset(par1, par2); 120 121 if (var3 == 0) 122 { 123 return null; 124 } 125 else 126 { 127 int var4 = var3 >> 8; 128 int var5 = var3 & 255; 129 130 if (var4 + var5 > this.sectorFree.size()) 131 { 132 return null; 133 } 134 else 135 { 136 this.dataFile.seek((long)(var4 * 4096)); 137 int var6 = this.dataFile.readInt(); 138 139 if (var6 > 4096 * var5) 140 { 141 return null; 142 } 143 else if (var6 <= 0) 144 { 145 return null; 146 } 147 else 148 { 149 byte var7 = this.dataFile.readByte(); 150 byte[] var8; 151 152 if (var7 == 1) 153 { 154 var8 = new byte[var6 - 1]; 155 this.dataFile.read(var8); 156 return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(var8)))); 157 } 158 else if (var7 == 2) 159 { 160 var8 = new byte[var6 - 1]; 161 this.dataFile.read(var8); 162 return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(var8)))); 163 } 164 else 165 { 166 return null; 167 } 168 } 169 } 170 } 171 } 172 catch (IOException var9) 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 var5 = this.getOffset(par1, par2); 195 int var6 = var5 >> 8; 196 int var7 = var5 & 255; 197 int var8 = (par4 + 5) / 4096 + 1; 198 199 if (var8 >= 256) 200 { 201 return; 202 } 203 204 if (var6 != 0 && var7 == var8) 205 { 206 this.write(var6, par3ArrayOfByte, par4); 207 } 208 else 209 { 210 int var9; 211 212 for (var9 = 0; var9 < var7; ++var9) 213 { 214 this.sectorFree.set(var6 + var9, Boolean.valueOf(true)); 215 } 216 217 var9 = this.sectorFree.indexOf(Boolean.valueOf(true)); 218 int var10 = 0; 219 int var11; 220 221 if (var9 != -1) 222 { 223 for (var11 = var9; var11 < this.sectorFree.size(); ++var11) 224 { 225 if (var10 != 0) 226 { 227 if (((Boolean)this.sectorFree.get(var11)).booleanValue()) 228 { 229 ++var10; 230 } 231 else 232 { 233 var10 = 0; 234 } 235 } 236 else if (((Boolean)this.sectorFree.get(var11)).booleanValue()) 237 { 238 var9 = var11; 239 var10 = 1; 240 } 241 242 if (var10 >= var8) 243 { 244 break; 245 } 246 } 247 } 248 249 if (var10 >= var8) 250 { 251 var6 = var9; 252 this.setOffset(par1, par2, var9 << 8 | var8); 253 254 for (var11 = 0; var11 < var8; ++var11) 255 { 256 this.sectorFree.set(var6 + var11, Boolean.valueOf(false)); 257 } 258 259 this.write(var6, par3ArrayOfByte, par4); 260 } 261 else 262 { 263 this.dataFile.seek(this.dataFile.length()); 264 var6 = this.sectorFree.size(); 265 266 for (var11 = 0; var11 < var8; ++var11) 267 { 268 this.dataFile.write(emptySector); 269 this.sectorFree.add(Boolean.valueOf(false)); 270 } 271 272 this.sizeDelta += 4096 * var8; 273 this.write(var6, par3ArrayOfByte, par4); 274 this.setOffset(par1, par2, var6 << 8 | var8); 275 } 276 } 277 278 this.setChunkTimestamp(par1, par2, (int)(System.currentTimeMillis() / 1000L)); 279 } 280 catch (IOException var12) 281 { 282 var12.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 }