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    }