    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 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}