    001package net.minecraft.client.gui;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.awt.image.BufferedImage;
    006import java.io.IOException;
    007import java.io.InputStream;
    008import java.text.Bidi;
    009import java.util.Arrays;
    010import java.util.Iterator;
    011import java.util.List;
    012import java.util.Random;
    013import javax.imageio.ImageIO;
    014import net.minecraft.client.renderer.RenderEngine;
    015import net.minecraft.client.renderer.Tessellator;
    016import net.minecraft.client.settings.GameSettings;
    017import net.minecraft.util.ChatAllowedCharacters;
    018import org.lwjgl.opengl.GL11;
    019
    020@SideOnly(Side.CLIENT)
    021public class FontRenderer
    022{
    023    /** Array of width of all the characters in default.png */
    024    private int[] charWidth = new int[256];
    025    public int fontTextureName = 0;
    026
    027    /** the height in pixels of default text */
    028    public int FONT_HEIGHT = 9;
    029    public Random fontRandom = new Random();
    030
    031    /**
    032     * Array of the start/end column (in upper/lower nibble) for every glyph in the /font directory.
    033     */
    034    private byte[] glyphWidth = new byte[65536];
    035
    036    /**
    037     * Array of GL texture ids for loaded glyph_XX.png images. Indexed by Unicode block (group of 256 chars).
    038     */
    039    private final int[] glyphTextureName = new int[256];
    040
    041    /**
    042     * Array of RGB triplets defining the 16 standard chat colors followed by 16 darker version of the same colors for
    043     * drop shadows.
    044     */
    045    private int[] colorCode = new int[32];
    046
    047    /**
    048     * The currently bound GL texture ID. Avoids unnecessary glBindTexture() for the same texture if it's already bound.
    049     */
    050    private int boundTextureName;
    051
    052    /** The RenderEngine used to load and setup glyph textures. */
    053    private final RenderEngine renderEngine;
    054
    055    /** Current X coordinate at which to draw the next character. */
    056    private float posX;
    057
    058    /** Current Y coordinate at which to draw the next character. */
    059    private float posY;
    060
    061    /**
    062     * If true, strings should be rendered with Unicode fonts instead of the default.png font
    063     */
    064    private boolean unicodeFlag;
    065
    066    /**
    067     * If true, the Unicode Bidirectional Algorithm should be run before rendering any string.
    068     */
    069    private boolean bidiFlag;
    070
    071    /** Used to specify new red value for the current color. */
    072    private float red;
    073
    074    /** Used to specify new blue value for the current color. */
    075    private float blue;
    076
    077    /** Used to specify new green value for the current color. */
    078    private float green;
    079
    080    /** Used to speify new alpha value for the current color. */
    081    private float alpha;
    082
    083    /** Text color of the currently rendering string. */
    084    private int textColor;
    085
    086    /** Set if the "k" style (random) is active in currently rendering string */
    087    private boolean randomStyle = false;
    088
    089    /** Set if the "l" style (bold) is active in currently rendering string */
    090    private boolean boldStyle = false;
    091
    092    /** Set if the "o" style (italic) is active in currently rendering string */
    093    private boolean italicStyle = false;
    094
    095    /**
    096     * Set if the "n" style (underlined) is active in currently rendering string
    097     */
    098    private boolean underlineStyle = false;
    099
    100    /**
    101     * Set if the "m" style (strikethrough) is active in currently rendering string
    102     */
    103    private boolean strikethroughStyle = false;
    104
    105    FontRenderer()
    106    {
    107        this.renderEngine = null;
    108    }
    109
    110    public FontRenderer(GameSettings par1GameSettings, String par2Str, RenderEngine par3RenderEngine, boolean par4)
    111    {
    112        this.renderEngine = par3RenderEngine;
    113        this.unicodeFlag = par4;
    114        BufferedImage var5;
    115
    116        try
    117        {
    118            var5 = ImageIO.read(RenderEngine.class.getResourceAsStream(par2Str));
    119            InputStream var6 = RenderEngine.class.getResourceAsStream("/font/glyph_sizes.bin");
    120            var6.read(this.glyphWidth);
    121        }
    122        catch (IOException var18)
    123        {
    124            throw new RuntimeException(var18);
    125        }
    126
    127        int var19 = var5.getWidth();
    128        int var7 = var5.getHeight();
    129        int[] var8 = new int[var19 * var7];
    130        var5.getRGB(0, 0, var19, var7, var8, 0, var19);
    131        int var9 = 0;
    132        int var10;
    133        int var11;
    134        int var12;
    135        int var13;
    136        int var15;
    137        int var16;
    138
    139        while (var9 < 256)
    140        {
    141            var10 = var9 % 16;
    142            var11 = var9 / 16;
    143            var12 = 7;
    144
    145            while (true)
    146            {
    147                if (var12 >= 0)
    148                {
    149                    var13 = var10 * 8 + var12;
    150                    boolean var14 = true;
    151
    152                    for (var15 = 0; var15 < 8 && var14; ++var15)
    153                    {
    154                        var16 = (var11 * 8 + var15) * var19;
    155                        int var17 = var8[var13 + var16] & 255;
    156
    157                        if (var17 > 0)
    158                        {
    159                            var14 = false;
    160                        }
    161                    }
    162
    163                    if (var14)
    164                    {
    165                        --var12;
    166                        continue;
    167                    }
    168                }
    169
    170                if (var9 == 32)
    171                {
    172                    var12 = 2;
    173                }
    174
    175                this.charWidth[var9] = var12 + 2;
    176                ++var9;
    177                break;
    178            }
    179        }
    180
    181        this.fontTextureName = par3RenderEngine.allocateAndSetupTexture(var5);
    182
    183        for (var9 = 0; var9 < 32; ++var9)
    184        {
    185            var10 = (var9 >> 3 & 1) * 85;
    186            var11 = (var9 >> 2 & 1) * 170 + var10;
    187            var12 = (var9 >> 1 & 1) * 170 + var10;
    188            var13 = (var9 >> 0 & 1) * 170 + var10;
    189
    190            if (var9 == 6)
    191            {
    192                var11 += 85;
    193            }
    194
    195            if (par1GameSettings.anaglyph)
    196            {
    197                int var20 = (var11 * 30 + var12 * 59 + var13 * 11) / 100;
    198                var15 = (var11 * 30 + var12 * 70) / 100;
    199                var16 = (var11 * 30 + var13 * 70) / 100;
    200                var11 = var20;
    201                var12 = var15;
    202                var13 = var16;
    203            }
    204
    205            if (var9 >= 16)
    206            {
    207                var11 /= 4;
    208                var12 /= 4;
    209                var13 /= 4;
    210            }
    211
    212            this.colorCode[var9] = (var11 & 255) << 16 | (var12 & 255) << 8 | var13 & 255;
    213        }
    214    }
    215
    216    /**
    217     * Pick how to render a single character and return the width used.
    218     */
    219    private float renderCharAtPos(int par1, char par2, boolean par3)
    220    {
    221        return par2 == 32 ? 4.0F : (par1 > 0 && !this.unicodeFlag ? this.renderDefaultChar(par1 + 32, par3) : this.renderUnicodeChar(par2, par3));
    222    }
    223
    224    /**
    225     * Render a single character with the default.png font at current (posX,posY) location...
    226     */
    227    private float renderDefaultChar(int par1, boolean par2)
    228    {
    229        float var3 = (float)(par1 % 16 * 8);
    230        float var4 = (float)(par1 / 16 * 8);
    231        float var5 = par2 ? 1.0F : 0.0F;
    232
    233        if (this.boundTextureName != this.fontTextureName)
    234        {
    235            GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.fontTextureName);
    236            this.boundTextureName = this.fontTextureName;
    237        }
    238
    239        float var6 = (float)this.charWidth[par1] - 0.01F;
    240        GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
    241        GL11.glTexCoord2f(var3 / 128.0F, var4 / 128.0F);
    242        GL11.glVertex3f(this.posX + var5, this.posY, 0.0F);
    243        GL11.glTexCoord2f(var3 / 128.0F, (var4 + 7.99F) / 128.0F);
    244        GL11.glVertex3f(this.posX - var5, this.posY + 7.99F, 0.0F);
    245        GL11.glTexCoord2f((var3 + var6) / 128.0F, var4 / 128.0F);
    246        GL11.glVertex3f(this.posX + var6 + var5, this.posY, 0.0F);
    247        GL11.glTexCoord2f((var3 + var6) / 128.0F, (var4 + 7.99F) / 128.0F);
    248        GL11.glVertex3f(this.posX + var6 - var5, this.posY + 7.99F, 0.0F);
    249        GL11.glEnd();
    250        return (float)this.charWidth[par1];
    251    }
    252
    253    /**
    254     * Load one of the /font/glyph_XX.png into a new GL texture and store the texture ID in glyphTextureName array.
    255     */
    256    private void loadGlyphTexture(int par1)
    257    {
    258        String var3 = String.format("/font/glyph_%02X.png", new Object[] {Integer.valueOf(par1)});
    259        BufferedImage var2;
    260
    261        try
    262        {
    263            var2 = ImageIO.read(RenderEngine.class.getResourceAsStream(var3));
    264        }
    265        catch (IOException var5)
    266        {
    267            throw new RuntimeException(var5);
    268        }
    269
    270        this.glyphTextureName[par1] = this.renderEngine.allocateAndSetupTexture(var2);
    271        this.boundTextureName = this.glyphTextureName[par1];
    272    }
    273
    274    /**
    275     * Render a single Unicode character at current (posX,posY) location using one of the /font/glyph_XX.png files...
    276     */
    277    private float renderUnicodeChar(char par1, boolean par2)
    278    {
    279        if (this.glyphWidth[par1] == 0)
    280        {
    281            return 0.0F;
    282        }
    283        else
    284        {
    285            int var3 = par1 / 256;
    286
    287            if (this.glyphTextureName[var3] == 0)
    288            {
    289                this.loadGlyphTexture(var3);
    290            }
    291
    292            if (this.boundTextureName != this.glyphTextureName[var3])
    293            {
    294                GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.glyphTextureName[var3]);
    295                this.boundTextureName = this.glyphTextureName[var3];
    296            }
    297
    298            int var4 = this.glyphWidth[par1] >>> 4;
    299            int var5 = this.glyphWidth[par1] & 15;
    300            float var6 = (float)var4;
    301            float var7 = (float)(var5 + 1);
    302            float var8 = (float)(par1 % 16 * 16) + var6;
    303            float var9 = (float)((par1 & 255) / 16 * 16);
    304            float var10 = var7 - var6 - 0.02F;
    305            float var11 = par2 ? 1.0F : 0.0F;
    306            GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
    307            GL11.glTexCoord2f(var8 / 256.0F, var9 / 256.0F);
    308            GL11.glVertex3f(this.posX + var11, this.posY, 0.0F);
    309            GL11.glTexCoord2f(var8 / 256.0F, (var9 + 15.98F) / 256.0F);
    310            GL11.glVertex3f(this.posX - var11, this.posY + 7.99F, 0.0F);
    311            GL11.glTexCoord2f((var8 + var10) / 256.0F, var9 / 256.0F);
    312            GL11.glVertex3f(this.posX + var10 / 2.0F + var11, this.posY, 0.0F);
    313            GL11.glTexCoord2f((var8 + var10) / 256.0F, (var9 + 15.98F) / 256.0F);
    314            GL11.glVertex3f(this.posX + var10 / 2.0F - var11, this.posY + 7.99F, 0.0F);
    315            GL11.glEnd();
    316            return (var7 - var6) / 2.0F + 1.0F;
    317        }
    318    }
    319
    320    /**
    321     * Draws the specified string with a shadow.
    322     */
    323    public int drawStringWithShadow(String par1Str, int par2, int par3, int par4)
    324    {
    325        return this.drawString(par1Str, par2, par3, par4, true);
    326    }
    327
    328    /**
    329     * Draws the specified string.
    330     */
    331    public int drawString(String par1Str, int par2, int par3, int par4)
    332    {
    333        return this.drawString(par1Str, par2, par3, par4, false);
    334    }
    335
    336    /**
    337     * Draws the specified string. Args: string, x, y, color, dropShadow
    338     */
    339    public int drawString(String par1Str, int par2, int par3, int par4, boolean par5)
    340    {
    341        this.resetStyles();
    342
    343        if (this.bidiFlag)
    344        {
    345            par1Str = this.bidiReorder(par1Str);
    346        }
    347
    348        int var6;
    349
    350        if (par5)
    351        {
    352            var6 = this.renderString(par1Str, par2 + 1, par3 + 1, par4, true);
    353            var6 = Math.max(var6, this.renderString(par1Str, par2, par3, par4, false));
    354        }
    355        else
    356        {
    357            var6 = this.renderString(par1Str, par2, par3, par4, false);
    358        }
    359
    360        return var6;
    361    }
    362
    363    /**
    364     * Apply Unicode Bidirectional Algorithm to string and return a new possibly reordered string for visual rendering.
    365     */
    366    private String bidiReorder(String par1Str)
    367    {
    368        if (par1Str != null && Bidi.requiresBidi(par1Str.toCharArray(), 0, par1Str.length()))
    369        {
    370            Bidi var2 = new Bidi(par1Str, -2);
    371            byte[] var3 = new byte[var2.getRunCount()];
    372            String[] var4 = new String[var3.length];
    373            int var7;
    374
    375            for (int var5 = 0; var5 < var3.length; ++var5)
    376            {
    377                int var6 = var2.getRunStart(var5);
    378                var7 = var2.getRunLimit(var5);
    379                int var8 = var2.getRunLevel(var5);
    380                String var9 = par1Str.substring(var6, var7);
    381                var3[var5] = (byte)var8;
    382                var4[var5] = var9;
    383            }
    384
    385            String[] var11 = (String[])var4.clone();
    386            Bidi.reorderVisually(var3, 0, var4, 0, var3.length);
    387            StringBuilder var12 = new StringBuilder();
    388            var7 = 0;
    389
    390            while (var7 < var4.length)
    391            {
    392                byte var13 = var3[var7];
    393                int var14 = 0;
    394
    395                while (true)
    396                {
    397                    if (var14 < var11.length)
    398                    {
    399                        if (!var11[var14].equals(var4[var7]))
    400                        {
    401                            ++var14;
    402                            continue;
    403                        }
    404
    405                        var13 = var3[var14];
    406                    }
    407
    408                    if ((var13 & 1) == 0)
    409                    {
    410                        var12.append(var4[var7]);
    411                    }
    412                    else
    413                    {
    414                        for (var14 = var4[var7].length() - 1; var14 >= 0; --var14)
    415                        {
    416                            char var10 = var4[var7].charAt(var14);
    417
    418                            if (var10 == 40)
    419                            {
    420                                var10 = 41;
    421                            }
    422                            else if (var10 == 41)
    423                            {
    424                                var10 = 40;
    425                            }
    426
    427                            var12.append(var10);
    428                        }
    429                    }
    430
    431                    ++var7;
    432                    break;
    433                }
    434            }
    435
    436            return var12.toString();
    437        }
    438        else
    439        {
    440            return par1Str;
    441        }
    442    }
    443
    444    /**
    445     * Reset all style flag fields in the class to false; called at the start of string rendering
    446     */
    447    private void resetStyles()
    448    {
    449        this.randomStyle = false;
    450        this.boldStyle = false;
    451        this.italicStyle = false;
    452        this.underlineStyle = false;
    453        this.strikethroughStyle = false;
    454    }
    455
    456    /**
    457     * Render a single line string at the current (posX,posY) and update posX
    458     */
    459    private void renderStringAtPos(String par1Str, boolean par2)
    460    {
    461        for (int var3 = 0; var3 < par1Str.length(); ++var3)
    462        {
    463            char var4 = par1Str.charAt(var3);
    464            int var5;
    465            int var6;
    466
    467            if (var4 == 167 && var3 + 1 < par1Str.length())
    468            {
    469                var5 = "0123456789abcdefklmnor".indexOf(par1Str.toLowerCase().charAt(var3 + 1));
    470
    471                if (var5 < 16)
    472                {
    473                    this.randomStyle = false;
    474                    this.boldStyle = false;
    475                    this.strikethroughStyle = false;
    476                    this.underlineStyle = false;
    477                    this.italicStyle = false;
    478
    479                    if (var5 < 0 || var5 > 15)
    480                    {
    481                        var5 = 15;
    482                    }
    483
    484                    if (par2)
    485                    {
    486                        var5 += 16;
    487                    }
    488
    489                    var6 = this.colorCode[var5];
    490                    this.textColor = var6;
    491                    GL11.glColor4f((float)(var6 >> 16) / 255.0F, (float)(var6 >> 8 & 255) / 255.0F, (float)(var6 & 255) / 255.0F, this.alpha);
    492                }
    493                else if (var5 == 16)
    494                {
    495                    this.randomStyle = true;
    496                }
    497                else if (var5 == 17)
    498                {
    499                    this.boldStyle = true;
    500                }
    501                else if (var5 == 18)
    502                {
    503                    this.strikethroughStyle = true;
    504                }
    505                else if (var5 == 19)
    506                {
    507                    this.underlineStyle = true;
    508                }
    509                else if (var5 == 20)
    510                {
    511                    this.italicStyle = true;
    512                }
    513                else if (var5 == 21)
    514                {
    515                    this.randomStyle = false;
    516                    this.boldStyle = false;
    517                    this.strikethroughStyle = false;
    518                    this.underlineStyle = false;
    519                    this.italicStyle = false;
    520                    GL11.glColor4f(this.red, this.blue, this.green, this.alpha);
    521                }
    522
    523                ++var3;
    524            }
    525            else
    526            {
    527                var5 = ChatAllowedCharacters.allowedCharacters.indexOf(var4);
    528
    529                if (this.randomStyle && var5 > 0)
    530                {
    531                    do
    532                    {
    533                        var6 = this.fontRandom.nextInt(ChatAllowedCharacters.allowedCharacters.length());
    534                    }
    535                    while (this.charWidth[var5 + 32] != this.charWidth[var6 + 32]);
    536
    537                    var5 = var6;
    538                }
    539
    540                float var9 = this.renderCharAtPos(var5, var4, this.italicStyle);
    541
    542                if (this.boldStyle)
    543                {
    544                    ++this.posX;
    545                    this.renderCharAtPos(var5, var4, this.italicStyle);
    546                    --this.posX;
    547                    ++var9;
    548                }
    549
    550                Tessellator var7;
    551
    552                if (this.strikethroughStyle)
    553                {
    554                    var7 = Tessellator.instance;
    555                    GL11.glDisable(GL11.GL_TEXTURE_2D);
    556                    var7.startDrawingQuads();
    557                    var7.addVertex((double)this.posX, (double)(this.posY + (float)(this.FONT_HEIGHT / 2)), 0.0D);
    558                    var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)(this.FONT_HEIGHT / 2)), 0.0D);
    559                    var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)(this.FONT_HEIGHT / 2) - 1.0F), 0.0D);
    560                    var7.addVertex((double)this.posX, (double)(this.posY + (float)(this.FONT_HEIGHT / 2) - 1.0F), 0.0D);
    561                    var7.draw();
    562                    GL11.glEnable(GL11.GL_TEXTURE_2D);
    563                }
    564
    565                if (this.underlineStyle)
    566                {
    567                    var7 = Tessellator.instance;
    568                    GL11.glDisable(GL11.GL_TEXTURE_2D);
    569                    var7.startDrawingQuads();
    570                    int var8 = this.underlineStyle ? -1 : 0;
    571                    var7.addVertex((double)(this.posX + (float)var8), (double)(this.posY + (float)this.FONT_HEIGHT), 0.0D);
    572                    var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)this.FONT_HEIGHT), 0.0D);
    573                    var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)this.FONT_HEIGHT - 1.0F), 0.0D);
    574                    var7.addVertex((double)(this.posX + (float)var8), (double)(this.posY + (float)this.FONT_HEIGHT - 1.0F), 0.0D);
    575                    var7.draw();
    576                    GL11.glEnable(GL11.GL_TEXTURE_2D);
    577                }
    578
    579                this.posX += (float)((int)var9);
    580            }
    581        }
    582    }
    583
    584    /**
    585     * Render string either left or right aligned depending on bidiFlag
    586     */
    587    private int renderStringAligned(String par1Str, int par2, int par3, int par4, int par5, boolean par6)
    588    {
    589        if (this.bidiFlag)
    590        {
    591            par1Str = this.bidiReorder(par1Str);
    592            int var7 = this.getStringWidth(par1Str);
    593            par2 = par2 + par4 - var7;
    594        }
    595
    596        return this.renderString(par1Str, par2, par3, par5, par6);
    597    }
    598
    599    /**
    600     * Render single line string by setting GL color, current (posX,posY), and calling renderStringAtPos()
    601     */
    602    private int renderString(String par1Str, int par2, int par3, int par4, boolean par5)
    603    {
    604        if (par1Str == null)
    605        {
    606            return 0;
    607        }
    608        else
    609        {
    610            this.boundTextureName = 0;
    611
    612            if ((par4 & -67108864) == 0)
    613            {
    614                par4 |= -16777216;
    615            }
    616
    617            if (par5)
    618            {
    619                par4 = (par4 & 16579836) >> 2 | par4 & -16777216;
    620            }
    621
    622            this.red = (float)(par4 >> 16 & 255) / 255.0F;
    623            this.blue = (float)(par4 >> 8 & 255) / 255.0F;
    624            this.green = (float)(par4 & 255) / 255.0F;
    625            this.alpha = (float)(par4 >> 24 & 255) / 255.0F;
    626            GL11.glColor4f(this.red, this.blue, this.green, this.alpha);
    627            this.posX = (float)par2;
    628            this.posY = (float)par3;
    629            this.renderStringAtPos(par1Str, par5);
    630            return (int)this.posX;
    631        }
    632    }
    633
    634    /**
    635     * Returns the width of this string. Equivalent of FontMetrics.stringWidth(String s).
    636     */
    637    public int getStringWidth(String par1Str)
    638    {
    639        if (par1Str == null)
    640        {
    641            return 0;
    642        }
    643        else
    644        {
    645            int var2 = 0;
    646            boolean var3 = false;
    647
    648            for (int var4 = 0; var4 < par1Str.length(); ++var4)
    649            {
    650                char var5 = par1Str.charAt(var4);
    651                int var6 = this.getCharWidth(var5);
    652
    653                if (var6 < 0 && var4 < par1Str.length() - 1)
    654                {
    655                    ++var4;
    656                    var5 = par1Str.charAt(var4);
    657
    658                    if (var5 != 108 && var5 != 76)
    659                    {
    660                        if (var5 == 114 || var5 == 82)
    661                        {
    662                            var3 = false;
    663                        }
    664                    }
    665                    else
    666                    {
    667                        var3 = true;
    668                    }
    669
    670                    var6 = 0;
    671                }
    672
    673                var2 += var6;
    674
    675                if (var3)
    676                {
    677                    ++var2;
    678                }
    679            }
    680
    681            return var2;
    682        }
    683    }
    684
    685    /**
    686     * Returns the width of this character as rendered.
    687     */
    688    public int getCharWidth(char par1)
    689    {
    690        if (par1 == 167)
    691        {
    692            return -1;
    693        }
    694        else if (par1 == 32)
    695        {
    696            return 4;
    697        }
    698        else
    699        {
    700            int var2 = ChatAllowedCharacters.allowedCharacters.indexOf(par1);
    701
    702            if (var2 >= 0 && !this.unicodeFlag)
    703            {
    704                return this.charWidth[var2 + 32];
    705            }
    706            else if (this.glyphWidth[par1] != 0)
    707            {
    708                int var3 = this.glyphWidth[par1] >>> 4;
    709                int var4 = this.glyphWidth[par1] & 15;
    710
    711                if (var4 > 7)
    712                {
    713                    var4 = 15;
    714                    var3 = 0;
    715                }
    716
    717                ++var4;
    718                return (var4 - var3) / 2 + 1;
    719            }
    720            else
    721            {
    722                return 0;
    723            }
    724        }
    725    }
    726
    727    /**
    728     * Trims a string to fit a specified Width.
    729     */
    730    public String trimStringToWidth(String par1Str, int par2)
    731    {
    732        return this.trimStringToWidth(par1Str, par2, false);
    733    }
    734
    735    /**
    736     * Trims a string to a specified width, and will reverse it if par3 is set.
    737     */
    738    public String trimStringToWidth(String par1Str, int par2, boolean par3)
    739    {
    740        StringBuilder var4 = new StringBuilder();
    741        int var5 = 0;
    742        int var6 = par3 ? par1Str.length() - 1 : 0;
    743        int var7 = par3 ? -1 : 1;
    744        boolean var8 = false;
    745        boolean var9 = false;
    746
    747        for (int var10 = var6; var10 >= 0 && var10 < par1Str.length() && var5 < par2; var10 += var7)
    748        {
    749            char var11 = par1Str.charAt(var10);
    750            int var12 = this.getCharWidth(var11);
    751
    752            if (var8)
    753            {
    754                var8 = false;
    755
    756                if (var11 != 108 && var11 != 76)
    757                {
    758                    if (var11 == 114 || var11 == 82)
    759                    {
    760                        var9 = false;
    761                    }
    762                }
    763                else
    764                {
    765                    var9 = true;
    766                }
    767            }
    768            else if (var12 < 0)
    769            {
    770                var8 = true;
    771            }
    772            else
    773            {
    774                var5 += var12;
    775
    776                if (var9)
    777                {
    778                    ++var5;
    779                }
    780            }
    781
    782            if (var5 > par2)
    783            {
    784                break;
    785            }
    786
    787            if (par3)
    788            {
    789                var4.insert(0, var11);
    790            }
    791            else
    792            {
    793                var4.append(var11);
    794            }
    795        }
    796
    797        return var4.toString();
    798    }
    799
    800    /**
    801     * Remove all newline characters from the end of the string
    802     */
    803    private String trimStringNewline(String par1Str)
    804    {
    805        while (par1Str != null && par1Str.endsWith("\n"))
    806        {
    807            par1Str = par1Str.substring(0, par1Str.length() - 1);
    808        }
    809
    810        return par1Str;
    811    }
    812
    813    /**
    814     * Splits and draws a String with wordwrap (maximum length is parameter k)
    815     */
    816    public void drawSplitString(String par1Str, int par2, int par3, int par4, int par5)
    817    {
    818        this.resetStyles();
    819        this.textColor = par5;
    820        par1Str = this.trimStringNewline(par1Str);
    821        this.renderSplitString(par1Str, par2, par3, par4, false);
    822    }
    823
    824    /**
    825     * Perform actual work of rendering a multi-line string with wordwrap and with darker drop shadow color if flag is
    826     * set
    827     */
    828    private void renderSplitString(String par1Str, int par2, int par3, int par4, boolean par5)
    829    {
    830        List var6 = this.listFormattedStringToWidth(par1Str, par4);
    831
    832        for (Iterator var7 = var6.iterator(); var7.hasNext(); par3 += this.FONT_HEIGHT)
    833        {
    834            String var8 = (String)var7.next();
    835            this.renderStringAligned(var8, par2, par3, par4, this.textColor, par5);
    836        }
    837    }
    838
    839    /**
    840     * Returns the width of the wordwrapped String (maximum length is parameter k)
    841     */
    842    public int splitStringWidth(String par1Str, int par2)
    843    {
    844        return this.FONT_HEIGHT * this.listFormattedStringToWidth(par1Str, par2).size();
    845    }
    846
    847    /**
    848     * Set unicodeFlag controlling whether strings should be rendered with Unicode fonts instead of the default.png
    849     * font.
    850     */
    851    public void setUnicodeFlag(boolean par1)
    852    {
    853        this.unicodeFlag = par1;
    854    }
    855
    856    /**
    857     * Get unicodeFlag controlling whether strings should be rendered with Unicode fonts instead of the default.png
    858     * font.
    859     */
    860    public boolean getUnicodeFlag()
    861    {
    862        return this.unicodeFlag;
    863    }
    864
    865    /**
    866     * Set bidiFlag to control if the Unicode Bidirectional Algorithm should be run before rendering any string.
    867     */
    868    public void setBidiFlag(boolean par1)
    869    {
    870        this.bidiFlag = par1;
    871    }
    872
    873    /**
    874     * Breaks a string into a list of pieces that will fit a specified width.
    875     */
    876    public List listFormattedStringToWidth(String par1Str, int par2)
    877    {
    878        return Arrays.asList(this.wrapFormattedStringToWidth(par1Str, par2).split("\n"));
    879    }
    880
    881    /**
    882     * Inserts newline and formatting into a string to wrap it within the specified width.
    883     */
    884    String wrapFormattedStringToWidth(String par1Str, int par2)
    885    {
    886        int var3 = this.sizeStringToWidth(par1Str, par2);
    887
    888        if (par1Str.length() <= var3)
    889        {
    890            return par1Str;
    891        }
    892        else
    893        {
    894            String var4 = par1Str.substring(0, var3);
    895            char var5 = par1Str.charAt(var3);
    896            boolean var6 = var5 == 32 || var5 == 10;
    897            String var7 = getFormatFromString(var4) + par1Str.substring(var3 + (var6 ? 1 : 0));
    898            return var4 + "\n" + this.wrapFormattedStringToWidth(var7, par2);
    899        }
    900    }
    901
    902    /**
    903     * Determines how many characters from the string will fit into the specified width.
    904     */
    905    private int sizeStringToWidth(String par1Str, int par2)
    906    {
    907        int var3 = par1Str.length();
    908        int var4 = 0;
    909        int var5 = 0;
    910        int var6 = -1;
    911
    912        for (boolean var7 = false; var5 < var3; ++var5)
    913        {
    914            char var8 = par1Str.charAt(var5);
    915
    916            switch (var8)
    917            {
    918                case 10:
    919                    --var5;
    920                    break;
    921                case 167:
    922                    if (var5 < var3 - 1)
    923                    {
    924                        ++var5;
    925                        char var9 = par1Str.charAt(var5);
    926
    927                        if (var9 != 108 && var9 != 76)
    928                        {
    929                            if (var9 == 114 || var9 == 82 || isFormatColor(var9))
    930                            {
    931                                var7 = false;
    932                            }
    933                        }
    934                        else
    935                        {
    936                            var7 = true;
    937                        }
    938                    }
    939
    940                    break;
    941                case 32:
    942                    var6 = var5;
    943                default:
    944                    var4 += this.getCharWidth(var8);
    945
    946                    if (var7)
    947                    {
    948                        ++var4;
    949                    }
    950            }
    951
    952            if (var8 == 10)
    953            {
    954                ++var5;
    955                var6 = var5;
    956                break;
    957            }
    958
    959            if (var4 > par2)
    960            {
    961                break;
    962            }
    963        }
    964
    965        return var5 != var3 && var6 != -1 && var6 < var5 ? var6 : var5;
    966    }
    967
    968    /**
    969     * Checks if the char code is a hexadecimal character, used to set colour.
    970     */
    971    private static boolean isFormatColor(char par0)
    972    {
    973        return par0 >= 48 && par0 <= 57 || par0 >= 97 && par0 <= 102 || par0 >= 65 && par0 <= 70;
    974    }
    975
    976    /**
    977     * Checks if the char code is O-K...lLrRk-o... used to set special formatting.
    978     */
    979    private static boolean isFormatSpecial(char par0)
    980    {
    981        return par0 >= 107 && par0 <= 111 || par0 >= 75 && par0 <= 79 || par0 == 114 || par0 == 82;
    982    }
    983
    984    /**
    985     * Digests a string for nonprinting formatting characters then returns a string containing only that formatting.
    986     */
    987    private static String getFormatFromString(String par0Str)
    988    {
    989        String var1 = "";
    990        int var2 = -1;
    991        int var3 = par0Str.length();
    992
    993        while ((var2 = par0Str.indexOf(167, var2 + 1)) != -1)
    994        {
    995            if (var2 < var3 - 1)
    996            {
    997                char var4 = par0Str.charAt(var2 + 1);
    998
    999                if (isFormatColor(var4))
    1000                {
    1001                    var1 = "\u00a7" + var4;
    1002                }
    1003                else if (isFormatSpecial(var4))
    1004                {
    1005                    var1 = var1 + "\u00a7" + var4;
    1006                }
    1007            }
    1008        }
    1009
    1010        return var1;
    1011    }
    1012
    1013    /**
    1014     * Get bidiFlag that controls if the Unicode Bidirectional Algorithm should be run before rendering any string
    1015     */
    1016    public boolean getBidiFlag()
    1017    {
    1018        return this.bidiFlag;
    1019    }
    1020}