001package net.minecraft.util;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import java.io.DataInputStream;
006import java.io.DataOutputStream;
007import java.io.File;
008import java.io.FileOutputStream;
009import java.io.IOException;
010import java.net.URL;
011import java.net.URLConnection;
012
013import javax.xml.parsers.DocumentBuilder;
014import javax.xml.parsers.DocumentBuilderFactory;
015import net.minecraft.client.Minecraft;
016import org.w3c.dom.Document;
017import org.w3c.dom.Element;
018import org.w3c.dom.Node;
019import org.w3c.dom.NodeList;
020
021@SideOnly(Side.CLIENT)
022public class ThreadDownloadResources extends Thread
023{
024    /** The folder to store the resources in. */
025    public File resourcesFolder;
026
027    /** A reference to the Minecraft object. */
028    private Minecraft mc;
029
030    /** Set to true when Minecraft is closing down. */
031    private boolean closing = false;
032
033    public ThreadDownloadResources(File par1File, Minecraft par2Minecraft)
034    {
035        this.mc = par2Minecraft;
036        this.setName("Resource download thread");
037        this.setDaemon(true);
038        this.resourcesFolder = new File(par1File, "resources/");
039
040        if (!this.resourcesFolder.exists() && !this.resourcesFolder.mkdirs())
041        {
042            throw new RuntimeException("The working directory could not be created: " + this.resourcesFolder);
043        }
044    }
045
046    public void run()
047    {
048        try
049        {
050            URL url = new URL("http://s3.amazonaws.com/MinecraftResources/");
051            DocumentBuilderFactory documentbuilderfactory = DocumentBuilderFactory.newInstance();
052            DocumentBuilder documentbuilder = documentbuilderfactory.newDocumentBuilder();
053            //Add a timeout of 60 seconds to getting the list, MC stalls without sound for some users.
054            URLConnection con = url.openConnection();
055            con.setConnectTimeout(60000);
056            con.setReadTimeout(60000);
057            Document document = documentbuilder.parse(con.getInputStream());
058            NodeList nodelist = document.getElementsByTagName("Contents");
059
060            for (int i = 0; i < 2; ++i)
061            {
062                for (int j = 0; j < nodelist.getLength(); ++j)
063                {
064                    Node node = nodelist.item(j);
065
066                    if (node.getNodeType() == 1)
067                    {
068                        Element element = (Element)node;
069                        String s = element.getElementsByTagName("Key").item(0).getChildNodes().item(0).getNodeValue();
070                        long k = Long.parseLong(element.getElementsByTagName("Size").item(0).getChildNodes().item(0).getNodeValue());
071
072                        if (k > 0L)
073                        {
074                            this.downloadAndInstallResource(url, s, k, i);
075
076                            if (this.closing)
077                            {
078                                return;
079                            }
080                        }
081                    }
082                }
083            }
084        }
085        catch (Exception exception)
086        {
087            this.loadResource(this.resourcesFolder, "");
088            exception.printStackTrace();
089        }
090    }
091
092    /**
093     * Reloads the resource folder and passes the resources to Minecraft to install.
094     */
095    public void reloadResources()
096    {
097        this.loadResource(this.resourcesFolder, "");
098    }
099
100    /**
101     * Loads a resource and passes it to Minecraft to install.
102     */
103    private void loadResource(File par1File, String par2Str)
104    {
105        File[] afile = par1File.listFiles();
106
107        for (int i = 0; i < afile.length; ++i)
108        {
109            if (afile[i].isDirectory())
110            {
111                this.loadResource(afile[i], par2Str + afile[i].getName() + "/");
112            }
113            else
114            {
115                try
116                {
117                    this.mc.installResource(par2Str + afile[i].getName(), afile[i]);
118                }
119                catch (Exception exception)
120                {
121                    this.mc.getLogAgent().logWarning("Failed to add " + par2Str + afile[i].getName() + " in resources");
122                }
123            }
124        }
125    }
126
127    /**
128     * Downloads the resource and saves it to disk then installs it.
129     */
130    private void downloadAndInstallResource(URL par1URL, String par2Str, long par3, int par5)
131    {
132        try
133        {
134            int k = par2Str.indexOf("/");
135            String s1 = par2Str.substring(0, k);
136
137            if (s1.equalsIgnoreCase("sound3"))
138            {
139                if (par5 != 0)
140                {
141                    return;
142                }
143            }
144            else if (par5 != 1)
145            {
146                return;
147            }
148
149            File file1 = new File(this.resourcesFolder, par2Str);
150
151            if (!file1.exists() || file1.length() != par3)
152            {
153                file1.getParentFile().mkdirs();
154                String s2 = par2Str.replaceAll(" ", "%20");
155                this.downloadResource(new URL(par1URL, s2), file1, par3);
156
157                if (this.closing)
158                {
159                    return;
160                }
161            }
162
163            this.mc.installResource(par2Str, file1);
164        }
165        catch (Exception exception)
166        {
167            exception.printStackTrace();
168        }
169    }
170
171    /**
172     * Downloads the resource and saves it to disk.
173     */
174    private void downloadResource(URL par1URL, File par2File, long par3) throws IOException
175    {
176        byte[] abyte = new byte[4096];
177        //Add a timeout of 60 seconds to getting the list, MC stalls without sound for some users.
178        URLConnection con = par1URL.openConnection();
179        con.setConnectTimeout(60000);
180        con.setReadTimeout(60000);
181        DataInputStream datainputstream = new DataInputStream(con.getInputStream());
182        DataOutputStream dataoutputstream = new DataOutputStream(new FileOutputStream(par2File));
183        boolean flag = false;
184
185        do
186        {
187            int j;
188
189            if ((j = datainputstream.read(abyte)) < 0)
190            {
191                datainputstream.close();
192                dataoutputstream.close();
193                return;
194            }
195
196            dataoutputstream.write(abyte, 0, j);
197        }
198        while (!this.closing);
199    }
200
201    /**
202     * Called when Minecraft is closing down.
203     */
204    public void closeMinecraft()
205    {
206        this.closing = true;
207    }
208}