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