    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    }