TileEntitySignRendererMod(Mod mod) {
      super(mod);
      RenderUtilsMod.setup(this);

      final FieldRef sign;
      final MethodRef colorizeSignText =
          new MethodRef(MCPatcherUtils.COLORIZE_WORLD_CLASS, "colorizeSignText", "()I");

      if (ResourceLocationMod.haveClass()) {
        sign = new FieldRef(getDeobfClass(), "sign", "LResourceLocation;");
        addClassSignature(new ResourceLocationSignature(this, sign, "textures/entity/sign.png"));
      } else {
        sign = null;
        addClassSignature(new ConstSignature("/item/sign.png"));
      }

      addPatch(
          new BytecodePatch() {
            {
              addPreMatchSignature(
                  new BytecodeSignature() {
                    @Override
                    public String getMatchExpression() {
                      return buildExpression(
                          sign == null ? push("/item/sign.png") : reference(GETSTATIC, sign));
                    }
                  });
            }

            @Override
            public String getDescription() {
              return "override sign text color";
            }

            @Override
            public String getMatchExpression() {
              return buildExpression(
                  push(0), RenderUtilsMod.glDepthMask(this), push(0), capture(anyISTORE));
            }

            @Override
            public byte[] getReplacementBytes() {
              return buildCode(
                  push(0),
                  RenderUtilsMod.glDepthMask(this),
                  reference(INVOKESTATIC, colorizeSignText),
                  getCaptureGroup(1));
            }
          });
    }
    EntityRendererMod(Mod mod) {
      super(mod);

      final MethodRef updateLightmap = new MethodRef(getDeobfClass(), "updateLightmap", "(F)V");
      final FieldRef mc = new FieldRef(getDeobfClass(), "mc", "LMinecraft;");
      final MethodRef updateFogColor = new MethodRef(getDeobfClass(), "updateFogColor", "(F)V");
      final FieldRef fogColorRed = new FieldRef(getDeobfClass(), "fogColorRed", "F");
      final FieldRef fogColorGreen = new FieldRef(getDeobfClass(), "fogColorGreen", "F");
      final FieldRef fogColorBlue = new FieldRef(getDeobfClass(), "fogColorBlue", "F");
      final FieldRef lightmapColors = new FieldRef(getDeobfClass(), "lightmapColors", "[I");
      final FieldRef lightmapTexture =
          new FieldRef(
              getDeobfClass(),
              "lightmapTexture",
              ResourceLocationMod.select("I", "LDynamicTexture;"));
      final FieldRef needLightmapUpdate = new FieldRef(getDeobfClass(), "needLightmapUpdate", "Z");
      final FieldRef renderEngine = new FieldRef("Minecraft", "renderEngine", "LRenderEngine;");
      final MethodRef createTextureFromBytes =
          new MethodRef("RenderEngine", "createTextureFromBytes", "([IIII)V");
      final FieldRef thePlayer = new FieldRef("Minecraft", "thePlayer", "LEntityClientPlayerMP;");
      final FieldRef nightVision = new FieldRef("Potion", "nightVision", "LPotion;");
      final MethodRef isPotionActive =
          new MethodRef("EntityClientPlayerMP", "isPotionActive", "(LPotion;)Z");
      final String nvEntity =
          getMinecraftVersion().compareTo("14w06a") >= 0 ? "LEntityLivingBase;" : "LEntityPlayer;";
      final MethodRef getNightVisionStrength1 =
          new MethodRef(getDeobfClass(), "getNightVisionStrength1", "(" + nvEntity + "F)F");
      final MethodRef getNightVisionStrength =
          new MethodRef(getDeobfClass(), "getNightVisionStrength", "(F)F");
      final MethodRef reloadTexture = new MethodRef("DynamicTexture", "reload", "()V");
      final MethodRef computeLightmap =
          new MethodRef(
              MCPatcherUtils.LIGHTMAP_CLASS, "computeLightmap", "(LEntityRenderer;LWorld;[IF)Z");
      final MethodRef computeUnderwaterColor =
          new MethodRef(MCPatcherUtils.COLORIZE_WORLD_CLASS, "computeUnderwaterColor", "()Z");
      final MethodRef computeUnderlavaColor =
          new MethodRef(MCPatcherUtils.COLORIZE_WORLD_CLASS, "computeUnderlavaColor", "()Z");

      addClassSignature(new ConstSignature("ambient.weather.rain"));

      addClassSignature(
          new BytecodeSignature() {
            {
              setMethod(updateLightmap);
              addXref(1, new MethodRef("World", "getSunAngle", "(F)F"));
              addXref(3, new FieldRef("World", "worldProvider", "LWorldProvider;"));
              addXref(5, new FieldRef(getDeobfClass(), "torchFlickerX", "F"));
              addXref(6, com.prupe.mcpatcher.basemod.WorldMod.getLightningFlashRef());
              addXref(7, com.prupe.mcpatcher.basemod.WorldProviderMod.getWorldTypeRef());
              addXref(8, mc);
              addXref(9, new FieldRef("Minecraft", "gameSettings", "LGameSettings;"));
              addXref(10, new FieldRef("GameSettings", "gammaSetting", "F"));
            }

            @Override
            public String getMatchExpression() {
              return buildExpression(
                  // sun = world.getSunAngle(1.0f) * 0.95f + 0.05f;
                  ALOAD_2,
                  push(1.0f),
                  captureReference(INVOKEVIRTUAL),
                  optional(build(anyFSTORE, anyFLOAD)), // 1.8.1-pre1
                  push(0.95f),
                  FMUL,
                  push(0.05f),
                  FADD,
                  FSTORE,
                  capture(any()),

                  // ... (1.8.1-pre1+)
                  any(0, 20),

                  // older: lightsun = world.worldProvider.lightBrightnessTable[i / 16] * sun;
                  // 14w02a+: lightsun = world.worldProvider.getLightBrightnessTable()[i / 16] *
                  // sun;
                  ALOAD_2,
                  captureReference(GETFIELD),
                  anyReference(GETFIELD, INVOKEVIRTUAL),
                  capture(anyILOAD),
                  push(16),
                  IDIV,
                  FALOAD,
                  FLOAD,
                  backReference(2),
                  FMUL,
                  anyFSTORE,

                  // older: lighttorch = world.worldProvider.lightBrightnessTable[i % 16] *
                  // (torchFlickerX * 0.1f + 1.5f);
                  // 14w02a+: lighttorch = world.worldProvider.getLightBrightnessTable()[i % 16] *
                  // (torchFlickerX * 0.1f + 1.5f);
                  any(0, 20),
                  backReference(4),
                  push(16),
                  IREM,
                  FALOAD,
                  ALOAD_0,
                  captureReference(GETFIELD),

                  // ...
                  any(0, 30),

                  // older: if (world.lightningFlash > 0)
                  // 14w02a+: if (world.getLightningFlash() > 0)
                  ALOAD_2,
                  captureReference(com.prupe.mcpatcher.basemod.WorldMod.getLightningFlashOpcode()),
                  IFLE,
                  any(2),

                  // ...
                  any(0, 300),

                  // older: if (world.worldProvider.worldType == 1) {
                  // 14w02a+: if (world.worldProvider.getWorldType() == 1) {
                  ALOAD_2,
                  backReference(3),
                  captureReference(
                      com.prupe.mcpatcher.basemod.WorldProviderMod.getWorldTypeOpcode()),
                  push(1),
                  IF_ICMPNE,
                  any(2),

                  // ...
                  any(0, 200),

                  // gamma = mc.gameSettings.gammaSetting;
                  ALOAD_0,
                  captureReference(GETFIELD),
                  captureReference(GETFIELD),
                  captureReference(GETFIELD),
                  anyFSTORE,

                  // ...
                  any(0, 300),
                  ResourceLocationMod.haveClass() ? getSubExpression16(10) : getSubExpression15(10),
                  RETURN);
            }

            private String getSubExpression15(int xref) {
              addXref(xref + 1, renderEngine);
              addXref(xref + 2, lightmapColors);
              addXref(xref + 3, lightmapTexture);
              addXref(xref + 4, createTextureFromBytes);
              return buildExpression(
                  // this.mc.renderEngine.createTextureFromBytes(this.lightmapColors, 16, 16,
                  // this.lightmapTexture);
                  ALOAD_0,
                  backReference(xref - 2),
                  captureReference(GETFIELD),
                  ALOAD_0,
                  captureReference(GETFIELD),
                  push(16),
                  push(16),
                  ALOAD_0,
                  captureReference(GETFIELD),
                  captureReference(INVOKEVIRTUAL));
            }

            private String getSubExpression16(int xref) {
              addXref(xref + 1, lightmapColors);
              addXref(xref + 2, lightmapTexture);
              addXref(xref + 3, reloadTexture);
              addXref(xref + 4, needLightmapUpdate);
              return buildExpression(
                  // this.lightmapColors[i] = ...;
                  ALOAD_0,
                  captureReference(GETFIELD),
                  any(0, 50),
                  IASTORE,

                  // ...
                  any(0, 20),

                  // this.lightmapTexture.load();
                  // this.needLightmapUpdate = false;
                  ALOAD_0,
                  captureReference(GETFIELD),
                  captureReference(INVOKEVIRTUAL),
                  ALOAD_0,
                  push(0),
                  captureReference(PUTFIELD),

                  // ...
                  any(0, 20));
            }
          });

      addClassSignature(
          new BytecodeSignature() {
            @Override
            public String getMatchExpression() {
              return buildExpression(
                  // fogColorRed = 0.02f;
                  ALOAD_0,
                  push(0.02f),
                  capture(
                      optional(
                          build( // 13w16a+
                              anyFLOAD, FADD))),
                  captureReference(PUTFIELD),

                  // fogColorGreen = 0.02f;
                  ALOAD_0,
                  push(0.02f),
                  backReference(1),
                  captureReference(PUTFIELD),

                  // fogColorBlue = 0.2f;
                  ALOAD_0,
                  push(0.2f),
                  backReference(1),
                  captureReference(PUTFIELD));
            }
          }.setMethod(updateFogColor)
              .addXref(2, fogColorRed)
              .addXref(3, fogColorGreen)
              .addXref(4, fogColorBlue));

      addClassSignature(
          new BytecodeSignature() {
            @Override
            public String getMatchExpression() {
              return buildExpression(
                  // if (mc.thePlayer.isPotionActive(Potion.nightVision)) {
                  capture(
                      build(
                          ALOAD_0,
                          captureReference(GETFIELD),
                          captureReference(GETFIELD),
                          captureReference(GETSTATIC),
                          captureReference(INVOKEVIRTUAL))),
                  IFEQ,
                  any(2),

                  // var16 = getNightVisionStrength1(mc.thePlayer, var1);
                  capture(
                      build(
                          ALOAD_0,
                          ALOAD_0,
                          backReference(2),
                          backReference(3),
                          FLOAD_1,
                          captureReference(INVOKESPECIAL))),
                  FSTORE,
                  any());
            }
          }.setMethod(updateLightmap)
              .addXref(2, mc)
              .addXref(3, thePlayer)
              .addXref(4, nightVision)
              .addXref(5, isPotionActive)
              .addXref(7, getNightVisionStrength1));

      addPatch(
          new AddMethodPatch(getNightVisionStrength) {
            @Override
            public byte[] generateMethod() {
              return buildCode(
                  ALOAD_0,
                  reference(GETFIELD, mc),
                  reference(GETFIELD, thePlayer),
                  reference(GETSTATIC, nightVision),
                  reference(INVOKEVIRTUAL, isPotionActive),
                  IFEQ,
                  branch("A"),
                  ALOAD_0,
                  ALOAD_0,
                  reference(GETFIELD, mc),
                  reference(GETFIELD, thePlayer),
                  FLOAD_1,
                  reference(INVOKESPECIAL, getNightVisionStrength1),
                  FRETURN,
                  label("A"),
                  push(0.0f),
                  FRETURN);
            }
          });

      addPatch(new MakeMemberPublicPatch(new FieldRef(getDeobfClass(), "torchFlickerX", "F")));

      addPatch(
          new BytecodePatch() {
            {
              setInsertAfter(true);
              targetMethod(updateLightmap);
            }

            @Override
            public String getDescription() {
              return "override lightmap";
            }

            @Override
            public String getMatchExpression() {
              return buildExpression(ASTORE_2);
            }

            @Override
            public byte[] getReplacementBytes() {
              return buildCode(
                  // if (Lightmap.computeLightmap(this, world, this.lightmapColors, partialTick)) {
                  ALOAD_0,
                  ALOAD_2,
                  ALOAD_0,
                  reference(GETFIELD, lightmapColors),
                  FLOAD_1,
                  reference(INVOKESTATIC, computeLightmap),
                  IFEQ,
                  branch("A"),
                  ResourceLocationMod.haveClass() ? loadTexture16() : loadTexture15(),

                  // return;
                  RETURN,

                  // }
                  label("A"));
            }

            private byte[] loadTexture15() {
              return buildCode(
                  // this.mc.renderEngine.createTextureFromBytes(this.lightmapColors, 16, 16,
                  // this.lightmapTexture);
                  ALOAD_0,
                  reference(GETFIELD, mc),
                  reference(GETFIELD, renderEngine),
                  ALOAD_0,
                  reference(GETFIELD, lightmapColors),
                  push(16),
                  push(16),
                  ALOAD_0,
                  reference(GETFIELD, lightmapTexture),
                  reference(INVOKEVIRTUAL, createTextureFromBytes));
            }

            private byte[] loadTexture16() {
              return buildCode(
                  // this.lightmapTexture.load();
                  // this.needLightmapUpdate = false;
                  ALOAD_0,
                  reference(GETFIELD, lightmapTexture),
                  reference(INVOKEVIRTUAL, reloadTexture),
                  ALOAD_0,
                  push(0),
                  reference(PUTFIELD, needLightmapUpdate));
            }
          });

      addPatch(
          new BytecodePatch() {
            {
              setInsertAfter(true);
              targetMethod(updateFogColor);
            }

            @Override
            public String getDescription() {
              return "override underwater ambient color";
            }

            @Override
            public String getMatchExpression() {
              return buildExpression(
                  // fogColorRed = 0.02f;
                  ALOAD_0,
                  push(0.02f),
                  capture(
                      optional(
                          build( // 13w16a+
                              anyFLOAD, FADD))),
                  reference(PUTFIELD, fogColorRed),

                  // fogColorGreen = 0.02f;
                  ALOAD_0,
                  push(0.02f),
                  backReference(1),
                  reference(PUTFIELD, fogColorGreen),

                  // fogColorBlue = 0.2f;
                  ALOAD_0,
                  push(0.2f),
                  backReference(1),
                  reference(PUTFIELD, fogColorBlue));
            }

            @Override
            public byte[] getReplacementBytes() {
              return buildCode(
                  // if (ColorizeWorld.computeUnderwaterColor()) {
                  reference(INVOKESTATIC, computeUnderwaterColor),
                  IFEQ,
                  branch("A"),

                  // fogColorRed = Colorizer.setColor[0];
                  ALOAD_0,
                  reference(GETSTATIC, setColor),
                  push(0),
                  FALOAD,
                  getCaptureGroup(1),
                  reference(PUTFIELD, fogColorRed),

                  // fogColorGreen = Colorizer.setColor[1];
                  ALOAD_0,
                  reference(GETSTATIC, setColor),
                  push(1),
                  FALOAD,
                  getCaptureGroup(1),
                  reference(PUTFIELD, fogColorGreen),

                  // fogColorBlue = Colorizer.setColor[2];
                  ALOAD_0,
                  reference(GETSTATIC, setColor),
                  push(2),
                  FALOAD,
                  getCaptureGroup(1),
                  reference(PUTFIELD, fogColorBlue),

                  // }
                  label("A"));
            }
          });

      addPatch(
          new BytecodePatch() {
            {
              setInsertAfter(true);
              targetMethod(updateFogColor);
            }

            @Override
            public String getDescription() {
              return "override underlava ambient color";
            }

            @Override
            public String getMatchExpression() {
              return buildExpression(
                  // this.fogColorRed = 0.6f;
                  ALOAD_0,
                  push(0.6f),
                  reference(PUTFIELD, fogColorRed),

                  // this.fogColorGreen = 0.1f;
                  ALOAD_0,
                  push(0.1f),
                  reference(PUTFIELD, fogColorGreen),

                  // this.fogColorBlue = 0.0f;
                  ALOAD_0,
                  push(0.0f),
                  reference(PUTFIELD, fogColorBlue));
            }

            @Override
            public byte[] getReplacementBytes() {
              return buildCode(
                  // if (ColorizeWorld.computeUnderlavaColor()) {
                  reference(INVOKESTATIC, computeUnderlavaColor),
                  IFEQ,
                  branch("A"),

                  // fogColorRed = Colorizer.setColor[0];
                  ALOAD_0,
                  reference(GETSTATIC, setColor),
                  push(0),
                  FALOAD,
                  reference(PUTFIELD, fogColorRed),

                  // fogColorGreen = Colorizer.setColor[1];
                  ALOAD_0,
                  reference(GETSTATIC, setColor),
                  push(1),
                  FALOAD,
                  reference(PUTFIELD, fogColorGreen),

                  // fogColorBlue = Colorizer.setColor[2];
                  ALOAD_0,
                  reference(GETSTATIC, setColor),
                  push(2),
                  FALOAD,
                  reference(PUTFIELD, fogColorBlue),

                  // }
                  label("A"));
            }
          });
    }
    RenderGlobalMod(Mod mod) {
      super(mod);

      final FieldRef clouds;
      final FieldRef mc = new FieldRef(getDeobfClass(), "mc", "LMinecraft;");
      final FieldRef gameSettings = new FieldRef("Minecraft", "gameSettings", "LGameSettings;");
      final JavaRef fancyGraphics;
      final int fancyGraphicsOp;
      final String fancyGraphicsIf;
      final MethodRef drawFancyClouds;
      if (getMinecraftVersion().compareTo("1.8.1-pre4") < 0) {
        fancyGraphics = new FieldRef("GameSettings", "fancyGraphics", "Z");
        fancyGraphicsOp = GETFIELD;
        fancyGraphicsIf = buildExpression(IFEQ, any(2));
        drawFancyClouds =
            new MethodRef(MCPatcherUtils.COLORIZE_WORLD_CLASS, "drawFancyClouds", "(Z)Z");
      } else {
        fancyGraphics = new MethodRef("GameSettings", "fancyGraphics", "()I");
        fancyGraphicsOp = INVOKEVIRTUAL;
        fancyGraphicsIf = buildExpression(ICONST_2, IF_ICMPNE_or_IF_ICMPEQ, any(2));
        drawFancyClouds =
            new MethodRef(MCPatcherUtils.COLORIZE_WORLD_CLASS, "drawFancyClouds", "(I)I");
      }
      final boolean intParam = getMinecraftVersion().compareTo("14w25a") >= 0;
      final MethodRef renderClouds =
          new MethodRef(getDeobfClass(), "renderClouds", "(F" + (intParam ? "I" : "") + ")V");
      final MethodRef renderCloudsFancy =
          new MethodRef(getDeobfClass(), "renderCloudsFancy", renderClouds.getType());
      final FieldRef endSkyColor =
          new FieldRef(MCPatcherUtils.COLORIZE_WORLD_CLASS, "endSkyColor", "I");

      RenderUtilsMod.setup(this);

      if (ResourceLocationMod.haveClass()) {
        clouds = new FieldRef(getDeobfClass(), "clouds", "LResourceLocation;");
        addClassSignature(
            new ResourceLocationSignature(this, clouds, "textures/environment/clouds.png"));
      } else {
        addClassSignature(new ConstSignature("/environment/clouds.png"));
        clouds = null;
      }

      addClassSignature(
          new BytecodeSignature() {
            {
              setMethod(renderClouds);
              addXref(1, mc);
              addXref(2, gameSettings);
              addXref(3, fancyGraphics);
              addXref(4, renderCloudsFancy);
              if (clouds != null) {
                addXref(5, clouds);
              }
            }

            @Override
            public String getMatchExpression() {
              return buildExpression(
                  // 1.8.1-pre4+: if (mc.gameSettings.fancyGraphics() == 2) {
                  // earlier: if (mc.gameSettings.fancyGraphics) {
                  ALOAD_0,
                  captureReference(GETFIELD),
                  captureReference(GETFIELD),
                  captureReference(fancyGraphicsOp),
                  fancyGraphicsIf,

                  // this.renderCloudsFancy(...);
                  ALOAD_0,
                  FLOAD_1,
                  intParam ? build(ILOAD_2) : "",
                  captureReference(INVOKESPECIAL, INVOKEVIRTUAL),
                  or(build(GOTO, any(2)), build(RETURN)),

                  // ...
                  any(0, 150),

                  // ...(RenderGlobal.clouds);
                  // ...("/environment/clouds.png");
                  clouds == null ? push("/environment/clouds.png") : captureReference(GETSTATIC));
            }
          });

      addPatch(
          new BytecodePatch() {
            @Override
            public String getDescription() {
              return "override cloud type";
            }

            @Override
            public String getMatchExpression() {
              return buildExpression(
                  capture(
                      build(
                          ALOAD_0,
                          reference(GETFIELD, mc),
                          reference(GETFIELD, gameSettings),
                          reference(fancyGraphicsOp, fancyGraphics))),
                  capture(fancyGraphicsIf));
            }

            @Override
            public byte[] getReplacementBytes() {
              return buildCode(
                  getCaptureGroup(1), reference(INVOKESTATIC, drawFancyClouds), getCaptureGroup(2));
            }
          }.targetMethod(renderClouds));

      if (TessellatorMod.haveVertexFormatClass()) {
        addPatch(
            new BytecodePatch() {
              @Override
              public String getDescription() {
                return "override end sky color";
              }

              @Override
              public String getMatchExpression() {
                return buildExpression(
                    // 40, 40, 40, 255
                    push(40), push(40), push(40), push(255));
              }

              @Override
              public byte[] getReplacementBytes() {
                return buildCode(
                    // (ColorizeWorld.endSkyColor >> 16) & 0xff
                    reference(GETSTATIC, endSkyColor),
                    push(16),
                    ISHR,
                    push(0xff),
                    IAND,

                    // (ColorizeWorld.endSkyColor >> 8) & 0xff
                    reference(GETSTATIC, endSkyColor),
                    push(8),
                    ISHR,
                    push(0xff),
                    IAND,

                    // ColorizeWorld.endSkyColor & 0xff
                    reference(GETSTATIC, endSkyColor),
                    push(0xff),
                    IAND,

                    // 255
                    push(255));
              }
            });
      } else {
        addPatch(
            new BytecodePatch() {
              @Override
              public String getDescription() {
                return "override end sky color";
              }

              @Override
              public String getMatchExpression() {
                return buildExpression(
                    or(
                        build(push(0x181818)), // pre-12w23a
                        build(push(0x282828)) // 12w23a+
                        ));
              }

              @Override
              public byte[] getReplacementBytes() {
                return buildCode(reference(GETSTATIC, endSkyColor));
              }
            });
      }
    }