    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    }