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 }