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