Decompiling Optifine

A step by step tutorial on how to decompile Optifine. Since the latest Optifine versions haven't been decompilied, this guide will help you get the latest Optifine.

## Pre-Decompiling and Decompiling

First, download the optifine version you want to decompile from the official website.

https://optifine.net/downloads

**Only download from the official source!**

Next, download the MCP that corresponds the Optifine version. For how to download non released versions with mappings, use this tutorial.

After you've extracted MCP, run the Optifine jar you just downloaded and press "Extract".

This will be the jar that you put in the mods folder if you want to use it in Forge, but it also includes the changed files, which is why its important.

Make sure you have ran MCP with the vanilla SRC at least once, and then run cleanup.bat. Then, in jars/versions/<MC VERSION HERE>, you can add the Optifine mod jar into the unmodded Minecraft jar file.

Next, decompile using decompile.bat. Follow the "Decompiling" instructions here.

**Note: You will see some errors. This is because Optifine adds some extra files and methods, which conflicts with the mappings.**

Open Eclipse. You will see errors. But don't worry. They aren't hard to fix!

## Fixing Errors

**Since notes are added from time to time, some of the corrections may not be necessary in later versions of Optifine.**

First, delete the package optifine, optifine.json, and optifine.xdelta. These are tools Optifine uses to inject into Forge, and since you are not using Forge, you don't need it.

Then, drag the unmodded Minecraft jar into the jars/versions/<MC VERSION> in your MCP folder.

You will also have to add javax.vecmath.Matrix4f and the optifine assets to the code.

I also like moving the net.minecraft.src to net.optifine. I usually do this AFTER I fix all the errors below.

There should be a considerable amount of errors that have built up here and there.

Here's a tutorial on how to fix them:

For **Type mismatch errors**, convert the the erroneous type into an object.

For example:

```
for (String s : this.listLines2)

```

turns into

```
for (Object s : this.listLines2)

```

and cast everything that calls this method back into strings.

For example:

```
this.drawCenteredString(this.fontRenderer, s, this.width / 2, i, 16777215);

```

turns into

```
this.drawCenteredString(this.fontRenderer, (String) s, this.width / 2, i, 16777215);

```

If the error is "Cannot convert List<X> into List<Y>, try to change one into the other. If you are confused, try tip 1.

For **duplicate variables**, use the Eclipse quick fix option to rename the error, but look in the notes for some exceptions.

For **bad casts**, there are different fixes depending on the type of bad cast error.

-If its an "E cannot be resolved" error, then remove the <E>.

-Also try the Eclipse quick fix option. It usually is correct.

-If its another type of error, try tip 1. Usually removing the cast solves it, but sometimes its more annoying.

-If null is being cast to an object, remove the cast.

There are a couple of other errors in it, but they should be self-explanatory and you could always look back at previously decompiled Optifines.

**Other errors:**

There is one error in GlStateManager that has a line called

```
fogMode = fogMode.capabilityId;

```

Remove that line if it is causing an error.

There is another error in TextureMap.java:

```
this.missingImage.setFramesTextureData(Lists.newArrayList(aint1));

```

To fix this, change the line to this:

```
this.missingImage.setFramesTextureData(Lists.newArrayList(new int[][][]{aint1}));

```

or this (1.12+)

```
this.missingImage.setFramesTextureData(Lists.<int[][]>newArrayList(aint1));

```

I have also posted notes at the bottom about some special cases.

***Tip 1: Look back at the vanilla decompiled source code (decompiling vanilla Minecraft) or previously decompiled Optifines at http://optifinesource.co.uk/.***

Now the question is: Are you done after the errors are gone? **No!** There are some other errors you have to fix.

First, try to run Optifine (Debug, and click client). Here are the errors I have encountered.

(P.S. If you get "no lwjgl in library path", try to see if the natives are there. For help, follow the "Missing Library/Natives" instructions here.)

**Hint: You can follow ~~https://www.youtube.com/watch?v=i\_8S99kEgd4~~(PRIVATE)** **for a tutorial! (NOTE: Do not follow it past makeBakedQuad)**

The first error is at makeBakedQuad in FaceBakery (net.minecraft.client.renderer.block.model). If you open the function you will see that all it does it run iteself over and over again until java runs out of memory.

Lets look at the next function. Also makeBakedQuad, but the one the makeBakedQuad is supposed to reference, but the decompiling ruined it.

In order to fix this, cast modelRotationIn to ITransformation, and it should point to that function instead.

Another function that has this error is **rotateVertex** in the same file, to fix that cast the last argument to ITransformation.

In new versions of Optifine, it is also possible that ModelBakery (same package as FaceBakery) can have an error. To fix this, fix the errors from these functions:

-bakeModel

-makeBakedQuad

(Run again)

The second error is the inability to select a shader. If you try to select another shader pack, it instantly crashes.

A stacktrace tells us that it is located in Shaders.java, loadShaderPack(). The function that causes the error is mc.scheduleResourcesRefresh(). It is also a NullPointerException, so something must have been null.

Turns out this is one of the decompiler issues, a quick search for when mc is initialized shows it:

http://pastebin.com/beEXmHB0

In order to fix it, do Shaders.mc = mc, which fixes everything.

**And that's it!**

## Notes

getWorldChanged in shadersmod/client/Shaders.java

This Eclipse quick fix option currently introduces a bug to the code, so here's the fixed code.

Note that there is a repeat if the quick fix is used, as shown below:

```
if (world1 != null && world1 != null)

```

Correct code: (NOTE: If setCameraOffset appears, also put that in)

```
    private static void checkWorldChanged(World world)
    {
        if (currentWorld != world)
        {
            World oldWorld = currentWorld;
            currentWorld = world;

            if (oldWorld != null && world != null)
            {
                int i = oldWorld.provider.getDimensionType().getId();
                int j = world.provider.getDimensionType().getId();
                boolean flag = shaderPackDimensions.contains(Integer.valueOf(i));
                boolean flag1 = shaderPackDimensions.contains(Integer.valueOf(j));

                if (flag || flag1)
                {
                    uninit();
                }
            }
        }
    }

```

A similar variable error occurs in getFunctionType in ExpressionParser.java, so here is the fixed code:

```
private EnumFunctionType getFunctionType(Token token, Deque<Token> deque) throws ParseException
    {
        Token token1 = (Token)deque.peek();

        if (token1 != null && token1.getType() == EnumTokenType.BRACKET_OPEN)
        {
            EnumFunctionType enumfunctiontype1 = EnumFunctionType.parse(token1.getText());
            checkNull(enumfunctiontype1, "Unknown function: " + token1);
            return enumfunctiontype1;
        }
        else
        {
            EnumFunctionType enumfunctiontype = EnumFunctionType.parse(token.getText());

            if (enumfunctiontype == null)
            {
                return null;
            }
            else if (enumfunctiontype.getCountArguments() > 0)
            {
                throw new ParseException("Missing arguments: " + enumfunctiontype);
            }
            else
            {
                return enumfunctiontype;
            }
        }
    }

```

And another at getGroup (same class):

```
private static Deque<Token> getGroup(Deque<Token> deque, EnumTokenType tokenTypeEnd, boolean tokenEndRequired) throws ParseException
    {
        Deque<Token> deque1 = new ArrayDeque();
        int i = 0;
        Iterator iterator = deque.iterator();

        while (iterator.hasNext())
        {
            Token token = (Token)iterator.next();
            iterator.remove();

            if (i == 0 && token.getType() == tokenTypeEnd)
            {
                return deque1;
            }

            deque1.add(token);

            if (token.getType() == EnumTokenType.BRACKET_OPEN)
            {
                ++i;
            }

            if (token.getType() == EnumTokenType.BRACKET_CLOSE)
            {
                --i;
            }
        }

        if (tokenEndRequired)
        {
            throw new ParseException("Missing end token: " + tokenTypeEnd);
        }
        else
        {
            return deque1;
        }
    }

```

Shaders.java has an error involving primitive variable names:

```
    private static int getDrawBuffer(int p, String str, int i)
    {
        int i1 = 0;

        if (i >= str.length())
        {
            return i1;
        }
        else
        {
            int j = str.charAt(i) - 48;

            if (p == 30)
            {
                if (j >= 0 && j <= 1)
                {
                    i1 = j + 36064;
                    usedShadowColorBuffers = Math.max(usedShadowColorBuffers, j);
                }

                return i1;
            }
            else
            {
                if (j >= 0 && j <= 7)
                {
                    programsToggleColorTextures[p][j] = true;
                    i1 = j + 36064;
                    usedColorAttachs = Math.max(usedColorAttachs, j);
                    usedColorBuffers = Math.max(usedColorBuffers, j);
                }

                return i1;
            }
        }
    }

```

Deobfuscating RenderChunk:

at RenderChunk, one of the errors is this:

```
if (ReflectorForge.blockHasTileEntity(iblockstate))

{

     TileEntity tileentity = chunkcacheof.getTileEntity(new BlockPos(blockposm));

```

            (Error at (new BlockPos(blockposm));)

            Eclipse encourages you to cast this to entity, but you should cast this to **BlockPosM**.

**LinkedList.java:**

```
    public String toString()
    {
    	StringBuffer stringbuffer = new StringBuffer();
        Iterator<LinkedList.Node<T>> iterator = iterator();

        while (iterator.hasNext())
        {
        	LinkedList.Node<T> node = (LinkedList.Node)iterator.next();

            if (stringbuffer.length() > 0)
            {
                stringbuffer.append(", ");
            }

            stringbuffer.append(node.getItem());
        }

        return "" + this.size + " [" + stringbuffer.toString() + "]";
    }

```

**LinkedListTest.java:**

```
    private static void dbgLinkedList(LinkedList<VboRange> p_dbgLinkedList_0_)
    {
        StringBuffer stringbuffer = new StringBuffer();
        Iterator<LinkedList.Node<VboRange>> iterator = p_dbgLinkedList_0_.iterator();

        while (iterator.hasNext())
        {
        	LinkedList.Node<VboRange> node = (LinkedList.Node)iterator.next();
            VboRange vborange = node.getItem();

            if (stringbuffer.length() > 0)
            {
                stringbuffer.append(", ");
            }

            stringbuffer.append(vborange.getPosition());
        }

        dbg("List: " + stringbuffer);
    }

```

            If there are any other errors, you should decompile Optifine.jar with FernFlower to check the code.