Package com.pclewis.mcpatcher.mod

Source Code of com.pclewis.mcpatcher.mod.HDFont$FontRendererMod

package com.pclewis.mcpatcher.mod;

import com.pclewis.mcpatcher.*;
import javassist.bytecode.ClassFile;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.MethodInfo;

import java.io.IOException;

import static com.pclewis.mcpatcher.BinaryRegex.*;
import static com.pclewis.mcpatcher.BytecodeMatcher.*;
import static javassist.bytecode.Opcode.*;

public class HDFont extends Mod {
    private final boolean haveAlternateFont;
    private final boolean haveFontHeight;
    private final boolean haveUnicode;
    private final boolean haveGetCharWidth;

    public HDFont(MinecraftVersion minecraftVersion) {
        name = MCPatcherUtils.HD_FONT;
        author = "MCPatcher";
        description = "Provides support for higher resolution fonts.";
        version = "1.4";

        addDependency(BaseTexturePackMod.NAME);

        haveAlternateFont = minecraftVersion.compareTo("Beta 1.9 Prerelease 3") >= 0;
        haveFontHeight = minecraftVersion.compareTo("Beta 1.9 Prerelease 6") >= 0;
        haveUnicode = minecraftVersion.compareTo("11w49a") >= 0 || minecraftVersion.compareTo("1.0.1") >= 0;
        haveGetCharWidth = minecraftVersion.compareTo("1.2.4") >= 0;

        addClassMod(new MinecraftMod());
        addClassMod(new FontRendererMod());
        addClassMod(new BaseMod.RenderEngineMod());
        addClassMod(new BaseMod.GameSettingsMod());

        addClassFile(MCPatcherUtils.FONT_UTILS_CLASS);
        addClassFile(MCPatcherUtils.FONT_UTILS_CLASS + "$1");
    }

    private class MinecraftMod extends BaseMod.MinecraftMod {
        MinecraftMod() {
            final FieldRef fontRenderer = new FieldRef(getDeobfClass(), "fontRenderer", "LFontRenderer;");
            final FieldRef alternateFontRenderer = new FieldRef(getDeobfClass(), "alternateFontRenderer", "LFontRenderer;");
            final FieldRef renderEngine = new FieldRef(getDeobfClass(), "renderEngine", "LRenderEngine;");
            final FieldRef gameSettings = new FieldRef(getDeobfClass(), "gameSettings", "LGameSettings;");

            if (haveAlternateFont) {
                addMemberMapper(new FieldMapper(fontRenderer, alternateFontRenderer));
            } else {
                addMemberMapper(new FieldMapper(fontRenderer));
                addMemberMapper(new FieldMapper(alternateFontRenderer));
            }
            addMemberMapper(new FieldMapper(renderEngine));
            addMemberMapper(new FieldMapper(gameSettings));
        }
    }

    private class FontRendererMod extends BaseMod.FontRendererMod {
        private final FieldRef charWidth = new FieldRef(getDeobfClass(), "charWidth", "[I");
        private final FieldRef charWidthf = new FieldRef(getDeobfClass(), "charWidthf", "[F");
        private final FieldRef fontHeight = new FieldRef(getDeobfClass(), "FONT_HEIGHT", "I");
        private final FieldRef isUnicode = new FieldRef(getDeobfClass(), "isUnicode", "Z");
        private final MethodRef getStringWidth = new MethodRef(getDeobfClass(), "getStringWidth", "(Ljava/lang/String;)I");
        private final MethodRef constructor = new MethodRef(getDeobfClass(), "<init>", "(LGameSettings;Ljava/lang/String;LRenderEngine;" + (haveUnicode ? "Z" : "") + ")V");

        FontRendererMod() {
            if (haveFontHeight) {
                addClassSignature(new BytecodeSignature() {
                    @Override
                    public String getMatchExpression() {
                        return buildExpression(
                            ALOAD_0,
                            or(
                                build(push(8)),
                                build(push(9))
                            ),
                            captureReference(PUTFIELD)
                        );
                    }
                }
                    .matchConstructorOnly(true)
                    .setMethod(constructor)
                    .addXref(1, fontHeight)
                );
            } else {
                addPatch(new AddFieldPatch(fontHeight));
            }

            if (haveUnicode) {
                addClassSignature(new BytecodeSignature() {
                    @Override
                    public String getMatchExpression() {
                        return buildExpression(
                            ALOAD_0,
                            ILOAD, 4,
                            captureReference(PUTFIELD)
                        );
                    }
                }
                    .matchConstructorOnly(true)
                    .addXref(1, isUnicode)
                );

                addPatch(new MakeMemberPublicPatch(isUnicode));
            } else {
                addPatch(new AddFieldPatch(isUnicode));
            }

            addMemberMapper(new MethodMapper(getStringWidth));

            addPatch(new AddFieldPatch(charWidthf));

            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "FontUtils.computeCharWidths on init";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        ICONST_0,
                        ISTORE, capture(any()),
                        ILOAD, backReference(1),
                        push(256),
                        IF_ICMPGE, any(2),
                        any(1, 180),
                        IINC, backReference(1), 1,
                        GOTO, any(2)
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    int registerOffset = getCaptureGroup(1)[0] - 8;
                    return buildCode(
                        ALOAD_0,
                        ALOAD_0,
                        ALOAD_2,
                        ALOAD, 4 + registerOffset,
                        ALOAD, 7 + registerOffset,
                        ALOAD_0,
                        reference(GETFIELD, charWidth),
                        reference(INVOKESTATIC, new MethodRef(MCPatcherUtils.FONT_UTILS_CLASS, "computeCharWidths", "(LFontRenderer;Ljava/lang/String;Ljava/awt/image/BufferedImage;[I[I)[F")),
                        reference(PUTFIELD, charWidthf)
                    );
                }
            });

            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "use charWidthf intead of charWidth";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        ALOAD_0,
                        reference(GETFIELD, charWidth),
                        capture(any(1, 4)),
                        IALOAD,
                        I2F,
                        FRETURN
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    return buildCode(
                        ALOAD_0,
                        reference(GETFIELD, charWidthf),
                        getCaptureGroup(1),
                        FALOAD,
                        ALOAD_0,
                        reference(GETFIELD, fontHeight),
                        I2F,
                        FMUL,
                        push(8.0f),
                        FDIV,
                        FRETURN
                    );
                }
            });

            addPatch(new AddMethodPatch(new MethodRef(getDeobfClass(), "initialize", "()V")) {
                MethodInfo constructor;

                @Override
                public void prePatch(ClassFile classFile) {
                    constructor = null;
                }

                @Override
                public byte[] generateMethod() {
                    getDescriptor();
                    CodeAttribute ca = constructor.getCodeAttribute();
                    getMethodInfo().setDescriptor(constructor.getDescriptor().replace("Z)", ")"));
                    maxStackSize = ca.getMaxStack();
                    numLocals = ca.getMaxLocals();
                    exceptionTable = ca.getExceptionTable();
                    byte[] code = ca.getCode().clone();
                    if (haveUnicode) {  // remove java.lang.Object<init> call
                        code[0] = ICONST_0;
                        code[1] = ISTORE;
                        code[2] = 4;
                    } else {
                        code[0] = NOP;
                        code[1] = NOP;
                        code[2] = NOP;
                    }
                    code[3] = NOP;
                    return code;
                }

                @Override
                public String getDescriptor() {
                    if (constructor == null) {
                        for (Object o : getClassFile().getMethods()) {
                            MethodInfo method = (MethodInfo) o;
                            if (method.isConstructor() &&
                                ((haveUnicode && method.getDescriptor().contains("Z)")) ||
                                    (!haveUnicode && !method.getDescriptor().equals("()V")))) {
                                constructor = method;
                                break;
                            }
                        }
                        if (constructor == null) {
                            throw new RuntimeException("could not find FontRenderer constructor");
                        }
                    }
                    return constructor.getDescriptor().replace("Z)", ")");
                }
            });

            if (haveGetCharWidth) {
                addStringWidthPatchesV2();
            } else {
                addStringWidthPatchesV1();
            }
        }

        private void addStringWidthPatchesV1() {
            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "use charWidthf in getStringWidth (init)";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        ICONST_0,
                        ISTORE_2
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    return buildCode(
                        FCONST_0,
                        FSTORE_2
                    );
                }
            }.targetMethod(getStringWidth));

            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "use charWidthf in getStringWidth (loop)";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        ILOAD_2,
                        ALOAD_0,
                        reference(GETFIELD, charWidth),
                        capture(anyILOAD),
                        BIPUSH, 32,
                        IADD,
                        IALOAD,
                        IADD,
                        ISTORE_2
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    return buildCode(
                        FLOAD_2,
                        ALOAD_0,
                        reference(GETFIELD, charWidthf),
                        getCaptureGroup(1),
                        BIPUSH, 32,
                        IADD,
                        FALOAD,
                        FADD,
                        FSTORE_2
                    );
                }
            }.targetMethod(getStringWidth));

            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "use charWidthf in getStringWidth (return value)";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        ILOAD_2,
                        IRETURN,
                        end()
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    return buildCode(
                        FLOAD_2,
                        reference(INVOKESTATIC, new MethodRef("java.lang.Math", "round", "(F)I")),
                        IRETURN
                    );
                }
            }.targetMethod(getStringWidth));

            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "getStringWidth int -> float";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        // k += (k1 - j1) / 2 + 1;
                        ILOAD_2,
                        capture(any(0, 20)),
                        IADD,
                        ISTORE_2
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    return buildCode(
                        FLOAD_2,
                        getCaptureGroup(1),
                        I2F,
                        FADD,
                        FSTORE_2
                    );
                }
            }.targetMethod(getStringWidth));

            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "4.0f -> charWidthf[32]";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        capture(build(
                            ALOAD_0,
                            DUP,
                            anyReference(GETFIELD)
                        )),
                        push(4.0f),
                        capture(build(
                            FADD,
                            anyReference(PUTFIELD)
                        ))
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    return buildCode(
                        getCaptureGroup(1),
                        ALOAD_0,
                        reference(GETFIELD, charWidthf),
                        push(32),
                        FALOAD,
                        getCaptureGroup(2)
                    );
                }
            });
        }

        private void addStringWidthPatchesV2() {
            final MethodRef getStringWidthf = new MethodRef(MCPatcherUtils.FONT_UTILS_CLASS, "getStringWidthf", "(LFontRenderer;Ljava/lang/String;)F");

            addMemberMapper(new MethodMapper(new MethodRef(getDeobfClass(), "getCharWidth", "(C)I")));

            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "replace getStringWidth";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        begin(),
                        any(0, 1000),
                        end()
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    return buildCode(
                        ALOAD_0,
                        ALOAD_1,
                        reference(INVOKESTATIC, getStringWidthf),
                        F2I,
                        IRETURN
                    );
                }
            }.targetMethod(getStringWidth));

            addPatch(new BytecodePatch() {
                @Override
                public String getDescription() {
                    return "4.0f -> charWidthf[32]";
                }

                @Override
                public String getMatchExpression() {
                    return buildExpression(
                        push(4.0f),
                        FRETURN
                    );
                }

                @Override
                public byte[] getReplacementBytes() throws IOException {
                    return buildCode(
                        ALOAD_0,
                        reference(GETFIELD, charWidthf),
                        push(32),
                        FALOAD,
                        FRETURN
                    );
                }
            });
        }
    }
}
TOP

Related Classes of com.pclewis.mcpatcher.mod.HDFont$FontRendererMod

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.