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    
655                    var2 += var6;
656    
657                    if (var3)
658                    {
659                        ++var2;
660                    }
661                }
662    
663                return var2;
664            }
665        }
666    
667        /**
668         * Returns the width of this character as rendered.
669         */
670        public int getCharWidth(char par1)
671        {
672            if (par1 == 167)
673            {
674                return -1;
675            }
676            else if (par1 == 32)
677            {
678                return 4;
679            }
680            else
681            {
682                int var2 = ChatAllowedCharacters.allowedCharacters.indexOf(par1);
683    
684                if (var2 >= 0 && !this.unicodeFlag)
685                {
686                    return this.charWidth[var2 + 32];
687                }
688                else if (this.glyphWidth[par1] != 0)
689                {
690                    int var3 = this.glyphWidth[par1] >>> 4;
691                    int var4 = this.glyphWidth[par1] & 15;
692    
693                    if (var4 > 7)
694                    {
695                        var4 = 15;
696                        var3 = 0;
697                    }
698    
699                    ++var4;
700                    return (var4 - var3) / 2 + 1;
701                }
702                else
703                {
704                    return 0;
705                }
706            }
707        }
708    
709        /**
710         * Trims a string to fit a specified Width.
711         */
712        public String trimStringToWidth(String par1Str, int par2)
713        {
714            return this.trimStringToWidth(par1Str, par2, false);
715        }
716    
717        /**
718         * Trims a string to a specified width, and will reverse it if par3 is set.
719         */
720        public String trimStringToWidth(String par1Str, int par2, boolean par3)
721        {
722            StringBuilder var4 = new StringBuilder();
723            int var5 = 0;
724            int var6 = par3 ? par1Str.length() - 1 : 0;
725            int var7 = par3 ? -1 : 1;
726            boolean var8 = false;
727            boolean var9 = false;
728    
729            for (int var10 = var6; var10 >= 0 && var10 < par1Str.length() && var5 < par2; var10 += var7)
730            {
731                char var11 = par1Str.charAt(var10);
732                int var12 = this.getCharWidth(var11);
733    
734                if (var8)
735                {
736                    var8 = false;
737    
738                    if (var11 != 108 && var11 != 76)
739                    {
740                        if (var11 == 114 || var11 == 82)
741                        {
742                            var9 = false;
743                        }
744                    }
745                    else
746                    {
747                        var9 = true;
748                    }
749                }
750                else if (var12 < 0)
751                {
752                    var8 = true;
753                }
754                else
755                {
756                    var5 += var12;
757    
758                    if (var9)
759                    {
760                        ++var5;
761                    }
762                }
763    
764                if (var5 > par2)
765                {
766                    break;
767                }
768    
769                if (par3)
770                {
771                    var4.insert(0, var11);
772                }
773                else
774                {
775                    var4.append(var11);
776                }
777            }
778    
779            return var4.toString();
780        }
781    
782        /**
783         * Remove all newline characters from the end of the string
784         */
785        private String trimStringNewline(String par1Str)
786        {
787            while (par1Str != null && par1Str.endsWith("\n"))
788            {
789                par1Str = par1Str.substring(0, par1Str.length() - 1);
790            }
791    
792            return par1Str;
793        }
794    
795        /**
796         * Splits and draws a String with wordwrap (maximum length is parameter k)
797         */
798        public void drawSplitString(String par1Str, int par2, int par3, int par4, int par5)
799        {
800            this.resetStyles();
801            this.textColor = par5;
802            par1Str = this.trimStringNewline(par1Str);
803            this.renderSplitStringNoShadow(par1Str, par2, par3, par4, par5);
804        }
805    
806        /**
807         * renders a multi-line string with wordwrap (maximum length is parameter k) by means of renderSplitString
808         */
809        private void renderSplitStringNoShadow(String par1Str, int par2, int par3, int par4, int par5)
810        {
811            this.textColor = par5;
812            this.renderSplitString(par1Str, par2, par3, par4, false);
813        }
814    
815        /**
816         * Perform actual work of rendering a multi-line string with wordwrap and with darker drop shadow color if flag is
817         * set
818         */
819        private void renderSplitString(String par1Str, int par2, int par3, int par4, boolean par5)
820        {
821            String[] var6 = par1Str.split("\n");
822    
823            if (var6.length > 1)
824            {
825                boolean var12 = false;
826                String[] var13 = var6;
827                int var14 = var6.length;
828    
829                for (int var15 = 0; var15 < var14; ++var15)
830                {
831                    String var16 = var13[var15];
832    
833                    if (var12)
834                    {
835                        var16 = "\u00a7" + var16;
836                        var12 = false;
837                    }
838    
839                    if (var16.endsWith("\u00a7"))
840                    {
841                        var12 = true;
842                        var16 = var16.substring(0, var16.length() - 1);
843                    }
844    
845                    this.renderSplitString(var16, par2, par3, par4, par5);
846                    par3 += this.splitStringWidth(var16, par4);
847                }
848            }
849            else
850            {
851                String[] var7 = par1Str.split(" ");
852                int var8 = 0;
853                String var9;
854    
855                for (var9 = ""; var8 < var7.length; ++var8)
856                {
857                    String var10 = var7[var8];
858    
859                    if (this.getStringWidth(var10) >= par4)
860                    {
861                        if (var9.length() > 0)
862                        {
863                            this.renderStringAligned(var9, par2, par3, par4, this.textColor, par5);
864                            par3 += this.FONT_HEIGHT;
865                        }
866    
867                        do
868                        {
869                            int var11;
870    
871                            for (var11 = 1; this.getStringWidth(var10.substring(0, var11)) < par4; ++var11)
872                            {
873                                ;
874                            }
875    
876                            this.renderStringAligned(var10.substring(0, var11 - 1), par2, par3, par4, this.textColor, par5);
877                            par3 += this.FONT_HEIGHT;
878                            var10 = var10.substring(var11 - 1);
879                        }
880                        while (this.getStringWidth(var10) >= par4);
881    
882                        var9 = var10;
883                    }
884                    else if (this.getStringWidth(var9 + " " + var10) >= par4)
885                    {
886                        this.renderStringAligned(var9, par2, par3, par4, this.textColor, par5);
887                        par3 += this.FONT_HEIGHT;
888                        var9 = var10;
889                    }
890                    else
891                    {
892                        if (var9.length() > 0)
893                        {
894                            var9 = var9 + " ";
895                        }
896    
897                        var9 = var9 + var10;
898                    }
899                }
900    
901                this.renderStringAligned(var9, par2, par3, par4, this.textColor, par5);
902            }
903        }
904    
905        /**
906         * Returns the width of the wordwrapped String (maximum length is parameter k)
907         */
908        public int splitStringWidth(String par1Str, int par2)
909        {
910            String[] var3 = par1Str.split("\n");
911            int var6;
912            String var8;
913    
914            if (var3.length > 1)
915            {
916                int var10 = 0;
917                String[] var11 = var3;
918                var6 = var3.length;
919    
920                for (int var12 = 0; var12 < var6; ++var12)
921                {
922                    var8 = var11[var12];
923                    var10 += this.splitStringWidth(var8, par2);
924                }
925    
926                return var10;
927            }
928            else
929            {
930                String[] var4 = par1Str.split(" ");
931                int var5 = 0;
932                var6 = 0;
933                String var7;
934    
935                for (var7 = ""; var6 < var4.length; ++var6)
936                {
937                    var8 = var4[var6];
938    
939                    if (this.getStringWidth(var8) >= par2)
940                    {
941                        if (var7.length() > 0)
942                        {
943                            var5 += this.FONT_HEIGHT;
944                        }
945    
946                        do
947                        {
948                            int var9;
949    
950                            for (var9 = 1; this.getStringWidth(var8.substring(0, var9)) < par2; ++var9)
951                            {
952                                ;
953                            }
954    
955                            var5 += this.FONT_HEIGHT;
956                            var8 = var8.substring(var9 - 1);
957                        }
958                        while (this.getStringWidth(var8) >= par2);
959    
960                        var7 = var8;
961                    }
962                    else if (this.getStringWidth(var7 + " " + var8) >= par2)
963                    {
964                        var5 += this.FONT_HEIGHT;
965                        var7 = var8;
966                    }
967                    else
968                    {
969                        if (var7.length() > 0)
970                        {
971                            var7 = var7 + " ";
972                        }
973    
974                        var7 = var7 + var8;
975                    }
976                }
977    
978                if (var7.length() > 0)
979                {
980                    var5 += this.FONT_HEIGHT;
981                }
982    
983                return var5;
984            }
985        }
986    
987        /**
988         * Set unicodeFlag controlling whether strings should be rendered with Unicode fonts instead of the default.png
989         * font.
990         */
991        public void setUnicodeFlag(boolean par1)
992        {
993            this.unicodeFlag = par1;
994        }
995    
996        public boolean func_82883_a()
997        {
998            return this.unicodeFlag;
999        }
1000    
1001        /**
1002         * Set bidiFlag to control if the Unicode Bidirectional Algorithm should be run before rendering any string.
1003         */
1004        public void setBidiFlag(boolean par1)
1005        {
1006            this.bidiFlag = par1;
1007        }
1008    
1009        /**
1010         * Breaks a string into a list of pieces that will fit a specified width.
1011         */
1012        public List listFormattedStringToWidth(String par1Str, int par2)
1013        {
1014            return Arrays.asList(this.wrapFormattedStringToWidth(par1Str, par2).split("\n"));
1015        }
1016    
1017        /**
1018         * Inserts newline and formatting into a string to wrap it within the specified width.
1019         */
1020        String wrapFormattedStringToWidth(String par1Str, int par2)
1021        {
1022            int var3 = this.sizeStringToWidth(par1Str, par2);
1023    
1024            if (par1Str.length() <= var3)
1025            {
1026                return par1Str;
1027            }
1028            else
1029            {
1030                String var4 = par1Str.substring(0, var3);
1031                String var5 = getFormatFromString(var4) + par1Str.substring(var3 + (par1Str.charAt(var3) == 32 ? 1 : 0));
1032                return var4 + "\n" + this.wrapFormattedStringToWidth(var5, par2);
1033            }
1034        }
1035    
1036        /**
1037         * Determines how many characters from the string will fit into the specified width.
1038         */
1039        private int sizeStringToWidth(String par1Str, int par2)
1040        {
1041            int var3 = par1Str.length();
1042            int var4 = 0;
1043            int var5 = 0;
1044            int var6 = -1;
1045    
1046            for (boolean var7 = false; var5 < var3; ++var5)
1047            {
1048                char var8 = par1Str.charAt(var5);
1049    
1050                switch (var8)
1051                {
1052                    case 167:
1053                        if (var5 < var3 - 1)
1054                        {
1055                            ++var5;
1056                            char var9 = par1Str.charAt(var5);
1057    
1058                            if (var9 != 108 && var9 != 76)
1059                            {
1060                                if (var9 == 114 || var9 == 82)
1061                                {
1062                                    var7 = false;
1063                                }
1064                            }
1065                            else
1066                            {
1067                                var7 = true;
1068                            }
1069                        }
1070    
1071                        break;
1072                    case 32:
1073                        var6 = var5;
1074                    default:
1075                        var4 += this.getCharWidth(var8);
1076    
1077                        if (var7)
1078                        {
1079                            ++var4;
1080                        }
1081                }
1082    
1083                if (var8 == 10)
1084                {
1085                    ++var5;
1086                    var6 = var5;
1087                    break;
1088                }
1089    
1090                if (var4 > par2)
1091                {
1092                    break;
1093                }
1094            }
1095    
1096            return var5 != var3 && var6 != -1 && var6 < var5 ? var6 : var5;
1097        }
1098    
1099        /**
1100         * Checks if the char code is a hexadecimal character, used to set colour.
1101         */
1102        private static boolean isFormatColor(char par0)
1103        {
1104            return par0 >= 48 && par0 <= 57 || par0 >= 97 && par0 <= 102 || par0 >= 65 && par0 <= 70;
1105        }
1106    
1107        /**
1108         * Checks if the char code is O-K...lLrRk-o... used to set special formatting.
1109         */
1110        private static boolean isFormatSpecial(char par0)
1111        {
1112            return par0 >= 107 && par0 <= 111 || par0 >= 75 && par0 <= 79 || par0 == 114 || par0 == 82;
1113        }
1114    
1115        /**
1116         * Digests a string for nonprinting formatting characters then returns a string containing only that formatting.
1117         */
1118        private static String getFormatFromString(String par0Str)
1119        {
1120            String var1 = "";
1121            int var2 = -1;
1122            int var3 = par0Str.length();
1123    
1124            while ((var2 = par0Str.indexOf(167, var2 + 1)) != -1)
1125            {
1126                if (var2 < var3 - 1)
1127                {
1128                    char var4 = par0Str.charAt(var2 + 1);
1129    
1130                    if (isFormatColor(var4))
1131                    {
1132                        var1 = "\u00a7" + var4;
1133                    }
1134                    else if (isFormatSpecial(var4))
1135                    {
1136                        var1 = var1 + "\u00a7" + var4;
1137                    }
1138                }
1139            }
1140    
1141            return var1;
1142        }
1143    
1144        /**
1145         * Get bidiFlag that controls if the Unicode Bidirectional Algorithm should be run before rendering any string
1146         */
1147        public boolean getBidiFlag()
1148        {
1149            return this.bidiFlag;
1150        }
1151    }