    001package net.minecraft.crash;
    002
    003import cpw.mods.fml.common.FMLCommonHandler;
    004import cpw.mods.fml.relauncher.Side;
    005import cpw.mods.fml.relauncher.SideOnly;
    006import java.io.File;
    007import java.io.FileWriter;
    008import java.io.IOException;
    009import java.io.PrintWriter;
    010import java.io.StringWriter;
    011import java.text.SimpleDateFormat;
    012import java.util.ArrayList;
    013import java.util.Date;
    014import java.util.Iterator;
    015import java.util.List;
    016import java.util.logging.Level;
    017import java.util.logging.Logger;
    018import net.minecraft.util.ReportedException;
    019
    020public 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}