001    package net.minecraft.src;
002    
003    import cpw.mods.fml.common.FMLCommonHandler;
004    import cpw.mods.fml.common.Side;
005    import cpw.mods.fml.common.asm.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    
019    public class CrashReport
020    {
021        /** Description of the crash report. */
022        private final String description;
023    
024        /** The Throwable that is the "cause" for this crash and Crash Report. */
025        private final Throwable cause;
026        private final CrashReportCategory field_85061_c = new CrashReportCategory(this, "System Details");
027    
028        /** Holds the keys and values of all crash report sections. */
029        private final List crashReportSections = new ArrayList();
030    
031        /** File of crash report. */
032        private File crashReportFile = null;
033        private boolean field_85059_f = true;
034        private StackTraceElement[] field_85060_g = new StackTraceElement[0];
035    
036        public CrashReport(String par1Str, Throwable par2Throwable)
037        {
038            this.description = par1Str;
039            this.cause = par2Throwable;
040            this.populateEnvironment();
041        }
042    
043        /**
044         * Populates this crash report with initial information about the running server and operating system / java
045         * environment
046         */
047        private void populateEnvironment()
048        {
049            this.field_85061_c.addCrashSectionCallable("Minecraft Version", new CallableMinecraftVersion(this));
050            this.field_85061_c.addCrashSectionCallable("Operating System", new CallableOSInfo(this));
051            this.field_85061_c.addCrashSectionCallable("Java Version", new CallableJavaInfo(this));
052            this.field_85061_c.addCrashSectionCallable("Java VM Version", new CallableJavaInfo2(this));
053            this.field_85061_c.addCrashSectionCallable("Memory", new CallableMemoryInfo(this));
054            this.field_85061_c.addCrashSectionCallable("JVM Flags", new CallableJVMFlags(this));
055            this.field_85061_c.addCrashSectionCallable("AABB Pool Size", new CallableCrashMemoryReport(this));
056            this.field_85061_c.addCrashSectionCallable("Suspicious classes", new CallableSuspiciousClasses(this));
057            this.field_85061_c.addCrashSectionCallable("IntCache", new CallableIntCache(this));
058            FMLCommonHandler.instance().enhanceCrashReport(this, this.field_85061_c);
059        }
060    
061        /**
062         * Returns the description of the Crash Report.
063         */
064        public String getDescription()
065        {
066            return this.description;
067        }
068    
069        /**
070         * Returns the Throwable object that is the cause for the crash and Crash Report.
071         */
072        public Throwable getCrashCause()
073        {
074            return this.cause;
075        }
076    
077        @SideOnly(Side.CLIENT)
078        public String func_90021_c()
079        {
080            StringBuilder var1 = new StringBuilder();
081            this.getSectionsInStringBuilder(var1);
082            return var1.toString();
083        }
084    
085        /**
086         * Gets the various sections of the crash report into the given StringBuilder
087         */
088        public void getSectionsInStringBuilder(StringBuilder par1StringBuilder)
089        {
090            if (this.field_85060_g != null && this.field_85060_g.length > 0)
091            {
092                par1StringBuilder.append("-- Head --\n");
093                par1StringBuilder.append("Stacktrace:\n");
094                StackTraceElement[] var2 = this.field_85060_g;
095                int var3 = var2.length;
096    
097                for (int var4 = 0; var4 < var3; ++var4)
098                {
099                    StackTraceElement var5 = var2[var4];
100                    par1StringBuilder.append("\t").append("at ").append(var5.toString());
101                    par1StringBuilder.append("\n");
102                }
103    
104                par1StringBuilder.append("\n");
105            }
106    
107            Iterator var6 = this.crashReportSections.iterator();
108    
109            while (var6.hasNext())
110            {
111                CrashReportCategory var7 = (CrashReportCategory)var6.next();
112                var7.func_85072_a(par1StringBuilder);
113                par1StringBuilder.append("\n\n");
114            }
115    
116            this.field_85061_c.func_85072_a(par1StringBuilder);
117        }
118    
119        /**
120         * Gets the stack trace of the Throwable that caused this crash report, or if that fails, the cause .toString().
121         */
122        public String getCauseStackTraceOrString()
123        {
124            StringWriter var1 = null;
125            PrintWriter var2 = null;
126            String var3 = this.cause.toString();
127    
128            try
129            {
130                var1 = new StringWriter();
131                var2 = new PrintWriter(var1);
132                this.cause.printStackTrace(var2);
133                var3 = var1.toString();
134            }
135            finally
136            {
137                try
138                {
139                    if (var1 != null)
140                    {
141                        var1.close();
142                    }
143    
144                    if (var2 != null)
145                    {
146                        var2.close();
147                    }
148                }
149                catch (IOException var10)
150                {
151                    ;
152                }
153            }
154    
155            return var3;
156        }
157    
158        /**
159         * Gets the complete report with headers, stack trace, and different sections as a string.
160         */
161        public String getCompleteReport()
162        {
163            StringBuilder var1 = new StringBuilder();
164            var1.append("---- Minecraft Crash Report ----\n");
165            var1.append("// ");
166            var1.append(getWittyComment());
167            var1.append("\n\n");
168            var1.append("Time: ");
169            var1.append((new SimpleDateFormat()).format(new Date()));
170            var1.append("\n");
171            var1.append("Description: ");
172            var1.append(this.description);
173            var1.append("\n\n");
174            var1.append(this.getCauseStackTraceOrString());
175            var1.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n");
176    
177            for (int var2 = 0; var2 < 87; ++var2)
178            {
179                var1.append("-");
180            }
181    
182            var1.append("\n\n");
183            this.getSectionsInStringBuilder(var1);
184            return var1.toString();
185        }
186    
187        @SideOnly(Side.CLIENT)
188    
189        /**
190         * Gets the file this crash report is saved into.
191         */
192        public File getFile()
193        {
194            return this.crashReportFile;
195        }
196    
197        /**
198         * Saves the complete crash report to the given File.
199         */
200        public boolean saveToFile(File par1File)
201        {
202            if (this.crashReportFile != null)
203            {
204                return false;
205            }
206            else
207            {
208                if (par1File.getParentFile() != null)
209                {
210                    par1File.getParentFile().mkdirs();
211                }
212    
213                try
214                {
215                    FileWriter var2 = new FileWriter(par1File);
216                    var2.write(this.getCompleteReport());
217                    var2.close();
218                    this.crashReportFile = par1File;
219                    return true;
220                }
221                catch (Throwable var3)
222                {
223                    Logger.getLogger("Minecraft").log(Level.SEVERE, "Could not save crash report to " + par1File, var3);
224                    return false;
225                }
226            }
227        }
228    
229        public CrashReportCategory func_85056_g()
230        {
231            return this.field_85061_c;
232        }
233    
234        public CrashReportCategory func_85058_a(String par1Str)
235        {
236            return this.func_85057_a(par1Str, 1);
237        }
238    
239        public CrashReportCategory func_85057_a(String par1Str, int par2)
240        {
241            CrashReportCategory var3 = new CrashReportCategory(this, par1Str);
242    
243            if (this.field_85059_f)
244            {
245                int var4 = var3.func_85073_a(par2);
246                StackTraceElement[] var5 = this.cause.getStackTrace();
247                StackTraceElement var6 = null;
248                StackTraceElement var7 = null;
249    
250                if (var5 != null && var5.length - var4 < var5.length)
251                {
252                    var6 = var5[var5.length - var4];
253    
254                    if (var5.length + 1 - var4 < var5.length)
255                    {
256                        var7 = var5[var5.length + 1 - var4];
257                    }
258                }
259    
260                this.field_85059_f = var3.func_85069_a(var6, var7);
261    
262                if (var4 > 0 && !this.crashReportSections.isEmpty())
263                {
264                    CrashReportCategory var8 = (CrashReportCategory)this.crashReportSections.get(this.crashReportSections.size() - 1);
265                    var8.func_85070_b(var4);
266                }
267                else if (var5 != null && var5.length >= var4)
268                {
269                    this.field_85060_g = new StackTraceElement[var5.length - var4];
270                    System.arraycopy(var5, 0, this.field_85060_g, 0, this.field_85060_g.length);
271                }
272                else
273                {
274                    this.field_85059_f = false;
275                }
276            }
277    
278            this.crashReportSections.add(var3);
279            return var3;
280        }
281    
282        /**
283         * Gets a random witty comment for inclusion in this CrashReport
284         */
285        private static String getWittyComment()
286        {
287            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!"};
288    
289            try
290            {
291                return var0[(int)(System.nanoTime() % (long)var0.length)];
292            }
293            catch (Throwable var2)
294            {
295                return "Witty comment unavailable :(";
296            }
297        }
298    
299        public static CrashReport func_85055_a(Throwable par0Throwable, String par1Str)
300        {
301            CrashReport var2;
302    
303            if (par0Throwable instanceof ReportedException)
304            {
305                var2 = ((ReportedException)par0Throwable).getCrashReport();
306            }
307            else
308            {
309                var2 = new CrashReport(par1Str, par0Throwable);
310            }
311    
312            return var2;
313        }
314    }