001    package net.minecraft.crash;
002    
003    import cpw.mods.fml.common.FMLCommonHandler;
004    import cpw.mods.fml.relauncher.Side;
005    import cpw.mods.fml.relauncher.SideOnly;
006    import java.io.File;
007    import java.io.FileWriter;
008    import java.io.IOException;
009    import java.io.PrintWriter;
010    import java.io.StringWriter;
011    import java.text.SimpleDateFormat;
012    import java.util.ArrayList;
013    import java.util.Date;
014    import java.util.Iterator;
015    import java.util.List;
016    import java.util.logging.Level;
017    import java.util.logging.Logger;
018    import net.minecraft.util.ReportedException;
019    
020    public class CrashReport
021    {
022        /** Description of the crash report. */
023        private final String description;
024    
025        /** The Throwable that is the "cause" for this crash and Crash Report. */
026        private final Throwable cause;
027        private final CrashReportCategory field_85061_c = new CrashReportCategory(this, "System Details");
028    
029        /** Holds the keys and values of all crash report sections. */
030        private final List crashReportSections = new ArrayList();
031    
032        /** File of crash report. */
033        private File crashReportFile = null;
034        private boolean field_85059_f = true;
035        private StackTraceElement[] field_85060_g = new StackTraceElement[0];
036    
037        public CrashReport(String par1Str, Throwable par2Throwable)
038        {
039            this.description = par1Str;
040            this.cause = par2Throwable;
041            this.populateEnvironment();
042        }
043    
044        /**
045         * Populates this crash report with initial information about the running server and operating system / java
046         * environment
047         */
048        private void populateEnvironment()
049        {
050            this.field_85061_c.addCrashSectionCallable("Minecraft Version", new CallableMinecraftVersion(this));
051            this.field_85061_c.addCrashSectionCallable("Operating System", new CallableOSInfo(this));
052            this.field_85061_c.addCrashSectionCallable("Java Version", new CallableJavaInfo(this));
053            this.field_85061_c.addCrashSectionCallable("Java VM Version", new CallableJavaInfo2(this));
054            this.field_85061_c.addCrashSectionCallable("Memory", new CallableMemoryInfo(this));
055            this.field_85061_c.addCrashSectionCallable("JVM Flags", new CallableJVMFlags(this));
056            this.field_85061_c.addCrashSectionCallable("AABB Pool Size", new CallableCrashMemoryReport(this));
057            this.field_85061_c.addCrashSectionCallable("Suspicious classes", new CallableSuspiciousClasses(this));
058            this.field_85061_c.addCrashSectionCallable("IntCache", new CallableIntCache(this));
059            FMLCommonHandler.instance().enhanceCrashReport(this, this.field_85061_c);
060        }
061    
062        /**
063         * Returns the description of the Crash Report.
064         */
065        public String getDescription()
066        {
067            return this.description;
068        }
069    
070        /**
071         * Returns the Throwable object that is the cause for the crash and Crash Report.
072         */
073        public Throwable getCrashCause()
074        {
075            return this.cause;
076        }
077    
078        @SideOnly(Side.CLIENT)
079        public String func_90021_c()
080        {
081            StringBuilder var1 = new StringBuilder();
082            this.getSectionsInStringBuilder(var1);
083            return var1.toString();
084        }
085    
086        /**
087         * Gets the various sections of the crash report into the given StringBuilder
088         */
089        public void getSectionsInStringBuilder(StringBuilder par1StringBuilder)
090        {
091            if (this.field_85060_g != null && this.field_85060_g.length > 0)
092            {
093                par1StringBuilder.append("-- Head --\n");
094                par1StringBuilder.append("Stacktrace:\n");
095                StackTraceElement[] var2 = this.field_85060_g;
096                int var3 = var2.length;
097    
098                for (int var4 = 0; var4 < var3; ++var4)
099                {
100                    StackTraceElement var5 = var2[var4];
101                    par1StringBuilder.append("\t").append("at ").append(var5.toString());
102                    par1StringBuilder.append("\n");
103                }
104    
105                par1StringBuilder.append("\n");
106            }
107    
108            Iterator var6 = this.crashReportSections.iterator();
109    
110            while (var6.hasNext())
111            {
112                CrashReportCategory var7 = (CrashReportCategory)var6.next();
113                var7.func_85072_a(par1StringBuilder);
114                par1StringBuilder.append("\n\n");
115            }
116    
117            this.field_85061_c.func_85072_a(par1StringBuilder);
118        }
119    
120        /**
121         * Gets the stack trace of the Throwable that caused this crash report, or if that fails, the cause .toString().
122         */
123        public String getCauseStackTraceOrString()
124        {
125            StringWriter var1 = null;
126            PrintWriter var2 = null;
127            String var3 = this.cause.toString();
128    
129            try
130            {
131                var1 = new StringWriter();
132                var2 = new PrintWriter(var1);
133                this.cause.printStackTrace(var2);
134                var3 = var1.toString();
135            }
136            finally
137            {
138                try
139                {
140                    if (var1 != null)
141                    {
142                        var1.close();
143                    }
144    
145                    if (var2 != null)
146                    {
147                        var2.close();
148                    }
149                }
150                catch (IOException var10)
151                {
152                    ;
153                }
154            }
155    
156            return var3;
157        }
158    
159        /**
160         * Gets the complete report with headers, stack trace, and different sections as a string.
161         */
162        public String getCompleteReport()
163        {
164            StringBuilder var1 = new StringBuilder();
165            var1.append("---- Minecraft Crash Report ----\n");
166            var1.append("// ");
167            var1.append(getWittyComment());
168            var1.append("\n\n");
169            var1.append("Time: ");
170            var1.append((new SimpleDateFormat()).format(new Date()));
171            var1.append("\n");
172            var1.append("Description: ");
173            var1.append(this.description);
174            var1.append("\n\n");
175            var1.append(this.getCauseStackTraceOrString());
176            var1.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n");
177    
178            for (int var2 = 0; var2 < 87; ++var2)
179            {
180                var1.append("-");
181            }
182    
183            var1.append("\n\n");
184            this.getSectionsInStringBuilder(var1);
185            return var1.toString();
186        }
187    
188        @SideOnly(Side.CLIENT)
189    
190        /**
191         * Gets the file this crash report is saved into.
192         */
193        public File getFile()
194        {
195            return this.crashReportFile;
196        }
197    
198        /**
199         * Saves the complete crash report to the given File.
200         */
201        public boolean saveToFile(File par1File)
202        {
203            if (this.crashReportFile != null)
204            {
205                return false;
206            }
207            else
208            {
209                if (par1File.getParentFile() != null)
210                {
211                    par1File.getParentFile().mkdirs();
212                }
213    
214                try
215                {
216                    FileWriter var2 = new FileWriter(par1File);
217                    var2.write(this.getCompleteReport());
218                    var2.close();
219                    this.crashReportFile = par1File;
220                    return true;
221                }
222                catch (Throwable var3)
223                {
224                    Logger.getLogger("Minecraft").log(Level.SEVERE, "Could not save crash report to " + par1File, var3);
225                    return false;
226                }
227            }
228        }
229    
230        public CrashReportCategory func_85056_g()
231        {
232            return this.field_85061_c;
233        }
234    
235        /**
236         * Creates a CrashReportCategory
237         */
238        public CrashReportCategory makeCategory(String par1Str)
239        {
240            return this.makeCategoryDepth(par1Str, 1);
241        }
242    
243        /**
244         * Creates a CrashReportCategory for the given stack trace depth
245         */
246        public CrashReportCategory makeCategoryDepth(String par1Str, int par2)
247        {
248            CrashReportCategory var3 = new CrashReportCategory(this, par1Str);
249    
250            if (this.field_85059_f)
251            {
252                int var4 = var3.func_85073_a(par2);
253                StackTraceElement[] var5 = this.cause.getStackTrace();
254                StackTraceElement var6 = null;
255                StackTraceElement var7 = null;
256    
257                if (var5 != null && var5.length - var4 < var5.length)
258                {
259                    var6 = var5[var5.length - var4];
260    
261                    if (var5.length + 1 - var4 < var5.length)
262                    {
263                        var7 = var5[var5.length + 1 - var4];
264                    }
265                }
266    
267                this.field_85059_f = var3.func_85069_a(var6, var7);
268    
269                if (var4 > 0 && !this.crashReportSections.isEmpty())
270                {
271                    CrashReportCategory var8 = (CrashReportCategory)this.crashReportSections.get(this.crashReportSections.size() - 1);
272                    var8.func_85070_b(var4);
273                }
274                else if (var5 != null && var5.length >= var4)
275                {
276                    this.field_85060_g = new StackTraceElement[var5.length - var4];
277                    System.arraycopy(var5, 0, this.field_85060_g, 0, this.field_85060_g.length);
278                }
279                else
280                {
281                    this.field_85059_f = false;
282                }
283            }
284    
285            this.crashReportSections.add(var3);
286            return var3;
287        }
288    
289        /**
290         * Gets a random witty comment for inclusion in this CrashReport
291         */
292        private static String getWittyComment()
293        {
294            String[] var0 = new String[] {"Who set us up the TNT?", "Everything\'s going to plan. No, really, that was supposed to happen.", "Uh... Did I do that?", "Oops.", "Why did you do that?", "I feel sad now :(", "My bad.", "I\'m sorry, Dave.", "I let you down. Sorry :(", "On the bright side, I bought you a teddy bear!", "Daisy, daisy...", "Oh - I know what I did wrong!", "Hey, that tickles! Hehehe!", "I blame Dinnerbone.", "You should try our sister game, Minceraft!", "Don\'t be sad. I\'ll do better next time, I promise!", "Don\'t be sad, have a hug! <3", "I just don\'t know what went wrong :(", "Shall we play a game?", "Quite honestly, I wouldn\'t worry myself about that.", "I bet Cylons wouldn\'t have this problem.", "Sorry :(", "Surprise! Haha. Well, this is awkward.", "Would you like a cupcake?", "Hi. I\'m Minecraft, and I\'m a crashaholic.", "Ooh. Shiny.", "This doesn\'t make any sense!", "Why is it breaking :(", "Don\'t do that.", "Ouch. That hurt :(", "You\'re mean.", "This is a token for 1 free hug. Redeem at your nearest Mojangsta: [~~HUG~~]", "There are four lights!"};
295    
296            try
297            {
298                return var0[(int)(System.nanoTime() % (long)var0.length)];
299            }
300            catch (Throwable var2)
301            {
302                return "Witty comment unavailable :(";
303            }
304        }
305    
306        /**
307         * Creates a crash report for the exception
308         */
309        public static CrashReport makeCrashReport(Throwable par0Throwable, String par1Str)
310        {
311            CrashReport var2;
312    
313            if (par0Throwable instanceof ReportedException)
314            {
315                var2 = ((ReportedException)par0Throwable).getCrashReport();
316            }
317            else
318            {
319                var2 = new CrashReport(par1Str, par0Throwable);
320            }
321    
322            return var2;
323        }
324    }