diff --git a/CREDITS b/CREDITS
index e72e0d4c..e2987d11 100644
--- a/CREDITS
+++ b/CREDITS
@@ -239,6 +239,58 @@
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
+ Project Name: Alfheim
+ Project Author: Red Studio
+ Project URL: https://github.com/Red-Studio-Ragnarok/Alfheim
+ 
+ Used For: Optimized lighting engine
+ 
+ * MIT License
+ *
+ * Copyright (c) 2023 Red Studio
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ 
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ 
+ Project Name: OptiFine (1.8.9_HD_U_M6)
+ Project Author: sp614x
+ Project URL: https://optifine.net/
+ 
+ Used For: A few, limited portions we "borrowed" without the author's permission
+ 
+ Note: Eaglercraft is not OptiFine, we only use the code for connected textures
+       and maintaining compatibility with certain resource pack features
+ 
+ * The mod OptiFine is Copyright © 2011-2021 by sp614x and the intellectual
+ * property of the author.
+ * It may be not be reproduced under any circumstances except for personal,
+ * private use as long as it remains in its unaltered, unedited form.
+ * It may not be placed on any web site or otherwise distributed publicly
+ * without advance written permission.
+ * Use of this mod on any other website or as a part of any public display
+ * is strictly prohibited and a violation of copyright.
+ 
+ (sorry sp614x)
+ 
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ 
  Project Name: Google Guava
  Project Author: Google
  Project URL: https://github.com/google/guava
diff --git a/client_version b/client_version
index 503c4b5b..ddc35e29 100644
--- a/client_version
+++ b/client_version
@@ -1 +1 @@
-u47
\ No newline at end of file
+u48
\ No newline at end of file
diff --git a/patches/minecraft/net/minecraft/block/Block.edit.java b/patches/minecraft/net/minecraft/block/Block.edit.java
index 4d942cde..79a0d980 100644
--- a/patches/minecraft/net/minecraft/block/Block.edit.java
+++ b/patches/minecraft/net/minecraft/block/Block.edit.java
@@ -5,12 +5,17 @@
 # Version: 1.0
 # Author: lax1dude
 
-> CHANGE  3 : 5  @  3 : 135
+> CHANGE  3 : 6  @  3 : 135
 
+~ 
 ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom;
 ~ 
 
-> CHANGE  354 : 355  @  354 : 355
+> INSERT  27 : 28  @  27
+
++ import net.minecraft.world.EnumSkyBlock;
+
+> CHANGE  327 : 328  @  327 : 328
 
 ~ 	public void randomTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) {
 
@@ -85,11 +90,49 @@
 + 	}
 + 
 
-> INSERT  43 : 47  @  43
+> INSERT  43 : 85  @  43
 
 + 
 + 	public boolean eaglerShadersShouldRenderGlassHighlights() {
 + 		return false;
 + 	}
++ 
++ 	public int alfheim$getLightFor(final IBlockState blockState, final IBlockAccess blockAccess,
++ 			final EnumSkyBlock lightType, final BlockPos blockPos) {
++ 		int lightLevel = blockAccess.getLightFor(lightType, blockPos);
++ 
++ 		if (lightLevel == 15)
++ 			return lightLevel;
++ 
++ 		if (!getUseNeighborBrightness())
++ 			return lightLevel;
++ 
++ 		BlockPos tmp = new BlockPos();
++ 		EnumFacing[] facings = EnumFacing._VALUES;
++ 		for (int i = 0, l = facings.length; i < l; ++i) {
++ 			EnumFacing facing = facings[i];
++ 			if (alfheim$useNeighborBrightness(blockState, facing, blockAccess, blockPos)) {
++ 				int opacity = 0;
++ 				final int neighborLightLevel = blockAccess.getLightFor(lightType,
++ 						blockPos.offsetEvenFaster(facing, tmp));
++ 
++ 				if (opacity == 0
++ 						&& (lightType != EnumSkyBlock.SKY || neighborLightLevel != EnumSkyBlock.SKY.defaultLightValue))
++ 					opacity = 1;
++ 
++ 				lightLevel = Math.max(lightLevel, neighborLightLevel - opacity);
++ 
++ 				if (lightLevel == 15)
++ 					return lightLevel;
++ 			}
++ 		}
++ 
++ 		return lightLevel;
++ 	}
++ 
++ 	public boolean alfheim$useNeighborBrightness(final IBlockState blockState, final EnumFacing facing,
++ 			final IBlockAccess blockAccess, final BlockPos blockPos) {
++ 		return facing == EnumFacing.UP;
++ 	}
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/block/BlockSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockSlab.edit.java
index a4fdd199..12300a9a 100644
--- a/patches/minecraft/net/minecraft/block/BlockSlab.edit.java
+++ b/patches/minecraft/net/minecraft/block/BlockSlab.edit.java
@@ -34,7 +34,7 @@
 
 ~ 	public int quantityDropped(EaglercraftRandom var1) {
 
-> INSERT  65 : 78  @  65
+> INSERT  65 : 89  @  65
 
 + 
 + 	public boolean onBlockActivated(World world, BlockPos blockpos, IBlockState var3, EntityPlayer entityplayer,
@@ -49,5 +49,16 @@
 + 		}
 + 		return super.onBlockActivated(world, blockpos, var3, entityplayer, var5, var6, var7, var8);
 + 	}
++ 
++ 	public boolean alfheim$useNeighborBrightness(final IBlockState blockState, final EnumFacing facing,
++ 			final IBlockAccess blockAccess, final BlockPos blockPos) {
++ 		if (isFullCube())
++ 			return false;
++ 
++ 		if (facing.getAxis() != EnumFacing.Axis.Y)
++ 			return true;
++ 
++ 		return facing == (blockState.getValue(HALF) == EnumBlockHalf.TOP ? EnumFacing.DOWN : EnumFacing.UP);
++ 	}
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/block/BlockStairs.edit.java b/patches/minecraft/net/minecraft/block/BlockStairs.edit.java
index da3726b4..c86ad037 100644
--- a/patches/minecraft/net/minecraft/block/BlockStairs.edit.java
+++ b/patches/minecraft/net/minecraft/block/BlockStairs.edit.java
@@ -60,4 +60,13 @@
 ~ 		for (int l = 0; l < amovingobjectposition.length; ++l) {
 ~ 			MovingObjectPosition movingobjectposition = amovingobjectposition[l];
 
+> INSERT  97 : 103  @  97
+
++ 
++ 	public boolean alfheim$useNeighborBrightness(final IBlockState blockState, final EnumFacing facing,
++ 			final IBlockAccess blockAccess, final BlockPos blockPos) {
++ 		return facing == (blockState.getValue(HALF) == EnumHalf.TOP ? EnumFacing.DOWN : EnumFacing.UP)
++ 				|| facing == blockState.getValue(FACING).getOpposite();
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java b/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java
index 18983b44..418f5cf0 100644
--- a/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java
+++ b/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java
@@ -15,10 +15,44 @@
 + import com.google.common.collect.Iterables;
 + 
 
-> DELETE  2  @  2 : 3
+> CHANGE  2 : 3  @  2 : 3
+
+~ import net.minecraft.util.ResourceLocation;
 
 > CHANGE  16 : 17  @  16 : 17
 
 ~ 				(T) cyclePropertyValue(property.getAllowedValues(), this.getValue(property)));
 
+> INSERT  30 : 59  @  30
+
++ 
++ 	private int blockId = -1;
++ 	private int blockStateId = -1;
++ 	private int metadata = -1;
++ 	private ResourceLocation blockLocation = null;
++ 
++ 	public int getBlockId() {
++ 		if (this.blockId < 0) {
++ 			this.blockId = Block.getIdFromBlock(this.getBlock());
++ 		}
++ 
++ 		return this.blockId;
++ 	}
++ 
++ 	public int getBlockStateId() {
++ 		if (this.blockStateId < 0) {
++ 			this.blockStateId = Block.getStateId(this);
++ 		}
++ 
++ 		return this.blockStateId;
++ 	}
++ 
++ 	public int getMetadata() {
++ 		if (this.metadata < 0) {
++ 			this.metadata = this.getBlock().getMetaFromState(this);
++ 		}
++ 
++ 		return this.metadata;
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java
index 9254dc94..3384cc2f 100644
--- a/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java
+++ b/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java
@@ -5,26 +5,29 @@
 # Version: 1.0
 # Author: lax1dude
 
-> INSERT  2 : 10  @  2
+> DELETE  2  @  2 : 8
 
-+ import java.util.ArrayList;
-+ import java.util.List;
-+ 
-+ import org.apache.commons.lang3.StringUtils;
-+ 
-+ import com.carrotsearch.hppc.CharObjectHashMap;
-+ import com.carrotsearch.hppc.CharObjectMap;
-+ import com.carrotsearch.hppc.cursors.CharObjectCursor;
+> INSERT  1 : 2  @  1
 
-> CHANGE  4 : 5  @  4 : 10
++ import java.util.HashMap;
+
+> CHANGE  3 : 4  @  3 : 6
 
 ~ 
 
-> DELETE  1  @  1 : 4
+> INSERT  2 : 9  @  2
 
-> CHANGE  4 : 5  @  4 : 5
++ import com.google.common.base.Joiner;
++ import com.google.common.base.Predicate;
++ import com.google.common.base.Predicates;
++ import com.google.common.collect.Lists;
++ 
++ import net.minecraft.block.state.BlockWorldState;
++ 
 
-~ 	private final CharObjectMap<Predicate<BlockWorldState>> symbolMap = new CharObjectHashMap<>();
+> CHANGE  3 : 4  @  3 : 4
+
+~ 	private final Map<Character, Predicate<BlockWorldState>> symbolMap = new HashMap<>();
 
 > CHANGE  4 : 5  @  4 : 5
 
@@ -59,10 +62,8 @@
 
 ~ 					apredicate[i][j][k] = this.symbolMap.get(((String[]) this.depth.get(i))[j].charAt(k));
 
-> CHANGE  10 : 13  @  10 : 13
+> CHANGE  10 : 11  @  10 : 11
 
-~ 		for (CharObjectCursor<Predicate<BlockWorldState>> entry : this.symbolMap) {
-~ 			if (entry.value == null) {
-~ 				arraylist.add(entry.key);
+~ 		for (Entry<Character, Predicate<BlockWorldState>> entry : this.symbolMap.entrySet()) {
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/Minecraft.edit.java b/patches/minecraft/net/minecraft/client/Minecraft.edit.java
index 434b4379..ad82de1f 100644
--- a/patches/minecraft/net/minecraft/client/Minecraft.edit.java
+++ b/patches/minecraft/net/minecraft/client/Minecraft.edit.java
@@ -149,7 +149,9 @@
 
 > DELETE  6  @  6 : 7
 
-> DELETE  1  @  1 : 19
+> CHANGE  1 : 2  @  1 : 19
+
+~ import net.optifine.Config;
 
 > CHANGE  1 : 2  @  1 : 2
 
@@ -272,10 +274,11 @@
 ~ 		} finally {
 ~ 			this.shutdownMinecraftApplet();
 
-> CHANGE  4 : 6  @  4 : 6
+> CHANGE  4 : 7  @  4 : 6
 
 ~ 	private void startGame() throws IOException {
 ~ 		this.gameSettings = new GameSettings(this);
+~ 		Config.setGameObj(this);
 
 > DELETE  1  @  1 : 2
 
@@ -1419,7 +1422,7 @@
 
 > DELETE  26  @  26 : 34
 
-> INSERT  7 : 44  @  7
+> INSERT  7 : 47  @  7
 
 + 
 + 	public static int getGLMaximumTextureSize() {
@@ -1458,5 +1461,8 @@
 + 		return EagRuntime.getConfiguration().isForceProfanityFilter() || gameSettings.enableProfanityFilter;
 + 	}
 + 
++ 	public DefaultResourcePack getDefaultResourcePack() {
++ 		return mcDefaultResourcePack;
++ 	}
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java
index 435d1b30..2d393a26 100644
--- a/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java
+++ b/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java
@@ -17,14 +17,13 @@
 
 + import java.util.Locale;
 
-> INSERT  1 : 18  @  1
+> INSERT  1 : 17  @  1
 
 + 
 + import org.apache.commons.lang3.StringUtils;
 + 
 + import java.util.TimeZone;
 + 
-+ import com.carrotsearch.hppc.cursors.ObjectCursor;
 + import com.google.common.base.Strings;
 + import com.google.common.collect.Lists;
 + 
@@ -108,7 +107,7 @@
 ~ 			}
 ~ 		}
 
-> INSERT  2 : 137  @  2
+> INSERT  2 : 136  @  2
 
 + 	private void drawFPS(int x, int y) {
 + 		this.fontRenderer.drawStringWithShadow(this.mc.renderGlobal.getDebugInfoShort(), x, y, 0xFFFFFF);
@@ -153,9 +152,8 @@
 + 		this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF);
 + 		i += 11;
 + 
-+ 		for (ObjectCursor<PotionEffect> ee : mc.thePlayer.getActivePotionEffects()) {
++ 		for (PotionEffect e : mc.thePlayer.getActivePotionEffectsList()) {
 + 			i += 11;
-+ 			PotionEffect e = ee.value;
 + 			int t = e.getDuration() / 20;
 + 			int m = t / 60;
 + 			int s = t % 60;
diff --git a/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java
index 4c30fae2..4fa589d6 100644
--- a/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java
+++ b/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java
@@ -21,16 +21,19 @@
 + 	 * + An array of all of GameSettings.Options's video options.
 + 	 */
 
-> CHANGE  2 : 10  @  2 : 7
+> CHANGE  2 : 13  @  2 : 7
 
 ~ 			GameSettings.Options.FRAMERATE_LIMIT, GameSettings.Options.EAGLER_VSYNC, GameSettings.Options.ANAGLYPH,
 ~ 			GameSettings.Options.VIEW_BOBBING, GameSettings.Options.GUI_SCALE, GameSettings.Options.GAMMA,
 ~ 			GameSettings.Options.RENDER_CLOUDS, GameSettings.Options.PARTICLES, GameSettings.Options.FXAA,
 ~ 			GameSettings.Options.MIPMAP_LEVELS, GameSettings.Options.BLOCK_ALTERNATIVES,
 ~ 			GameSettings.Options.ENTITY_SHADOWS, GameSettings.Options.FOG, GameSettings.Options.EAGLER_DYNAMIC_LIGHTS,
-~ 			GameSettings.Options.FULLSCREEN, GameSettings.Options.FNAW_SKINS, GameSettings.Options.HUD_FPS,
-~ 			GameSettings.Options.HUD_COORDS, GameSettings.Options.HUD_PLAYER, GameSettings.Options.HUD_STATS,
-~ 			GameSettings.Options.HUD_WORLD, GameSettings.Options.HUD_24H, GameSettings.Options.CHUNK_FIX };
+~ 			GameSettings.Options.FULLSCREEN, GameSettings.Options.OF_CONNECTED_TEXTURES,
+~ 			GameSettings.Options.OF_BETTER_GRASS, GameSettings.Options.OF_CUSTOM_SKIES,
+~ 			GameSettings.Options.OF_SMART_LEAVES, GameSettings.Options.OF_CUSTOM_ITEMS, GameSettings.Options.FNAW_SKINS,
+~ 			GameSettings.Options.HUD_FPS, GameSettings.Options.HUD_COORDS, GameSettings.Options.HUD_PLAYER,
+~ 			GameSettings.Options.HUD_STATS, GameSettings.Options.HUD_WORLD, GameSettings.Options.HUD_24H,
+~ 			GameSettings.Options.CHUNK_FIX };
 
 > CHANGE  11 : 18  @  11 : 22
 
diff --git a/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java b/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java
index b53dbfd8..30747432 100644
--- a/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java
+++ b/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java
@@ -23,7 +23,26 @@
 
 > DELETE  1  @  1 : 3
 
-> CHANGE  214 : 216  @  214 : 216
+> CHANGE  126 : 127  @  126 : 129
+
+~ 					GlStateManager.rotateZYXRad(this.rotateAngleX, this.rotateAngleY, this.rotateAngleZ);
+
+> DELETE  1  @  1 : 9
+
+> DELETE  25  @  25 : 28
+
+> CHANGE  1 : 3  @  1 : 4
+
+~ 				// note: vanilla order for this function is YXZ not ZYX for some reason
+~ 				GlStateManager.rotateZYXRad(this.rotateAngleX, this.rotateAngleY, this.rotateAngleZ);
+
+> DELETE  1  @  1 : 5
+
+> CHANGE  21 : 22  @  21 : 32
+
+~ 					GlStateManager.rotateZYXRad(this.rotateAngleX, this.rotateAngleY, this.rotateAngleZ);
+
+> CHANGE  7 : 9  @  7 : 9
 
 ~ 		this.displayList = GLAllocation.generateDisplayLists();
 ~ 		EaglercraftGPU.glNewList(this.displayList, GL_COMPILE);
diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java
index ac50601a..66a8ff98 100644
--- a/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java
+++ b/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java
@@ -55,4 +55,11 @@
 
 ~ 		return "MultiplayerChunkCache: " + this.chunkMapping.size() + ", " + this.chunkListing.size();
 
+> INSERT  20 : 24  @  20
+
++ 
++ 	public Chunk getLoadedChunk(int var1, int var2) {
++ 		return this.chunkMapping.get(ChunkCoordIntPair.chunkXZ2Int(var1, var2));
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java
index bee8911d..b229e828 100644
--- a/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java
@@ -16,25 +16,150 @@
 
 > DELETE  3  @  3 : 7
 
-> CHANGE  44 : 45  @  44 : 45
+> INSERT  8 : 9  @  8
 
-~ 		float[] afloat = new float[EnumFacing._VALUES.length * 2];
++ import net.minecraft.util.EnumWorldBlockLayer;
 
-> CHANGE  3 : 6  @  3 : 4
+> INSERT  4 : 9  @  4
+
++ import net.optifine.BetterSnow;
++ import net.optifine.Config;
++ import net.optifine.model.BlockModelCustomizer;
++ import net.optifine.model.ListQuadsOverlay;
++ import net.optifine.render.RenderEnv;
+
+> INSERT  2 : 5  @  2
+
++ 	private static final EnumWorldBlockLayer[] OVERLAY_LAYERS = new EnumWorldBlockLayer[] { EnumWorldBlockLayer.CUTOUT,
++ 			EnumWorldBlockLayer.CUTOUT_MIPPED, EnumWorldBlockLayer.TRANSLUCENT };
++ 
+
+> CHANGE  13 : 27  @  13 : 18
+
+~ 			RenderEnv renderenv = worldRendererIn.getRenderEnv(blockStateIn, blockPosIn);
+~ 			modelIn = BlockModelCustomizer.getRenderModel(modelIn, blockStateIn, renderenv);
+~ 			boolean flag1 = flag
+~ 					? this.renderModelAmbientOcclusion(blockAccessIn, modelIn, blockStateIn, blockPosIn,
+~ 							worldRendererIn, checkSides)
+~ 					: this.renderModelStandard(blockAccessIn, modelIn, blockStateIn, blockPosIn, worldRendererIn,
+~ 							checkSides);
+~ 
+~ 			if (flag1) {
+~ 				renderOverlayModels(blockAccessIn, modelIn, blockStateIn, blockPosIn, worldRendererIn, checkSides, 0L,
+~ 						renderenv, flag);
+~ 			}
+~ 
+~ 			return flag1;
+
+> CHANGE  9 : 72  @  9 : 11
+
+~ 	private void renderOverlayModels(IBlockAccess p_renderOverlayModels_1_, IBakedModel p_renderOverlayModels_2_,
+~ 			IBlockState p_renderOverlayModels_3_, BlockPos p_renderOverlayModels_4_,
+~ 			WorldRenderer p_renderOverlayModels_5_, boolean p_renderOverlayModels_6_, long p_renderOverlayModels_7_,
+~ 			RenderEnv p_renderOverlayModels_9_, boolean p_renderOverlayModels_10_) {
+~ 		if (p_renderOverlayModels_9_.isOverlaysRendered()) {
+~ 			for (int i = 0; i < OVERLAY_LAYERS.length; ++i) {
+~ 				EnumWorldBlockLayer enumworldblocklayer = OVERLAY_LAYERS[i];
+~ 				ListQuadsOverlay listquadsoverlay = p_renderOverlayModels_9_.getListQuadsOverlay(enumworldblocklayer);
+~ 
+~ 				if (listquadsoverlay.size() > 0) {
+~ 					RegionRenderCacheBuilder regionrendercachebuilder = p_renderOverlayModels_9_
+~ 							.getRegionRenderCacheBuilder();
+~ 
+~ 					if (regionrendercachebuilder != null) {
+~ 						WorldRenderer worldrenderer = regionrendercachebuilder
+~ 								.getWorldRendererByLayer(enumworldblocklayer);
+~ 
+~ //						if (!worldrenderer.isDrawing()) {
+~ //							worldrenderer.begin(7, DefaultVertexFormats.BLOCK);
+~ //							worldrenderer.setTranslation(p_renderOverlayModels_5_.getXOffset(),
+~ //									p_renderOverlayModels_5_.getYOffset(), p_renderOverlayModels_5_.getZOffset());
+~ //						}
+~ 
+~ 						for (int j = 0; j < listquadsoverlay.size(); ++j) {
+~ 							BakedQuad bakedquad = listquadsoverlay.getQuad(j);
+~ 							List<BakedQuad> list = listquadsoverlay.getListQuadsSingle(bakedquad);
+~ 							IBlockState iblockstate = listquadsoverlay.getBlockState(j);
+~ 
+~ //							if (bakedquad.getQuadEmissive() != null) {
+~ //								listquadsoverlay.addQuad(bakedquad.getQuadEmissive(), iblockstate);
+~ //							}
+~ 
+~ 							p_renderOverlayModels_9_.reset(iblockstate, p_renderOverlayModels_4_);
+~ 
+~ 							if (p_renderOverlayModels_10_) {
+~ 								this.renderModelAmbientOcclusionQuads(p_renderOverlayModels_1_, iblockstate,
+~ 										p_renderOverlayModels_4_, worldrenderer, list, p_renderOverlayModels_9_);
+~ 							} else {
+~ 								Block block = iblockstate.getBlock();
+~ 								int k = block.getMixedBrightnessForBlock(p_renderOverlayModels_1_,
+~ 										p_renderOverlayModels_4_.offset(bakedquad.getFace()));
+~ 								this.renderModelStandardQuads(p_renderOverlayModels_1_, block, p_renderOverlayModels_4_,
+~ 										bakedquad.getFace(), k, false, worldrenderer, list, p_renderOverlayModels_9_);
+~ 							}
+~ 						}
+~ 					}
+~ 
+~ 					listquadsoverlay.clear();
+~ 				}
+~ 			}
+~ 		}
+~ 
+~ 		if (Config.isBetterSnow() && !p_renderOverlayModels_9_.isBreakingAnimation() && BetterSnow
+~ 				.shouldRender(p_renderOverlayModels_1_, p_renderOverlayModels_3_, p_renderOverlayModels_4_)) {
+~ 			IBakedModel ibakedmodel = BetterSnow.getModelSnowLayer();
+~ 			IBlockState iblockstate1 = BetterSnow.getStateSnowLayer();
+~ 			this.renderModel(p_renderOverlayModels_1_, ibakedmodel, iblockstate1, p_renderOverlayModels_4_,
+~ 					p_renderOverlayModels_5_, p_renderOverlayModels_6_);
+~ 		}
+~ 	}
+~ 
+~ 	public boolean renderModelAmbientOcclusion(IBlockAccess blockAccessIn, IBakedModel modelIn,
+~ 			IBlockState blockStateIn, BlockPos blockPosIn, WorldRenderer worldRendererIn, boolean checkSides) {
+
+> CHANGE  1 : 4  @  1 : 4
+
+~ 		Block blockIn = blockStateIn.getBlock();
+~ 		RenderEnv renderenv = worldRendererIn.getRenderEnv(blockStateIn, blockPosIn);
+~ 		EnumWorldBlockLayer enumworldblocklayer = worldRendererIn.getBlockLayer();
+
+> CHANGE  1 : 4  @  1 : 2
 
 ~ 		EnumFacing[] facings = EnumFacing._VALUES;
 ~ 		for (int i = 0; i < facings.length; ++i) {
 ~ 			EnumFacing enumfacing = facings[i];
 
-> INSERT  23 : 24  @  23
+> CHANGE  4 : 8  @  4 : 6
 
-+ 		boolean isDeferred = DeferredStateManager.isDeferredRenderer();
+~ 					list = BlockModelCustomizer.getRenderQuads(list, blockAccessIn, blockStateIn, blockPosIn,
+~ 							enumfacing, enumworldblocklayer, 0L, renderenv);
+~ 					this.renderModelAmbientOcclusionQuads(blockAccessIn, blockStateIn, blockPosIn, worldRendererIn,
+~ 							list, renderenv);
 
-> INSERT  1 : 2  @  1
+> CHANGE  7 : 11  @  7 : 9
 
-+ 		float[] afloat = isDeferred ? new float[EnumFacing._VALUES.length * 2] : null;
+~ 			list1 = BlockModelCustomizer.getRenderQuads(list1, blockAccessIn, blockStateIn, blockPosIn,
+~ 					(EnumFacing) null, enumworldblocklayer, 0L, renderenv);
+~ 			this.renderModelAmbientOcclusionQuads(blockAccessIn, blockStateIn, blockPosIn, worldRendererIn, list1,
+~ 					renderenv);
 
-> CHANGE  2 : 6  @  2 : 3
+> CHANGE  6 : 7  @  6 : 7
+
+~ 	public boolean renderModelStandard(IBlockAccess blockAccessIn, IBakedModel modelIn, IBlockState blockStateIn,
+
+> INSERT  1 : 4  @  1
+
++ 		// boolean isDeferred = DeferredStateManager.isDeferredRenderer();
++ 		// float[] afloat = isDeferred ? new float[EnumFacing._VALUES.length * 2] :
++ 		// null;
+
+> CHANGE  1 : 4  @  1 : 2
+
+~ 		Block blockIn = blockStateIn.getBlock();
+~ 		RenderEnv renderenv = worldRendererIn.getRenderEnv(blockStateIn, blockPosIn);
+~ 		EnumWorldBlockLayer enumworldblocklayer = worldRendererIn.getBlockLayer();
+
+> CHANGE  1 : 5  @  1 : 2
 
 ~ 		BlockPos pointer = new BlockPos();
 ~ 		EnumFacing[] facings = EnumFacing._VALUES;
@@ -45,18 +170,34 @@
 
 ~ 				BlockPos blockpos = blockPosIn.offsetEvenFaster(enumfacing, pointer);
 
-> CHANGE  3 : 4  @  3 : 4
+> INSERT  2 : 4  @  2
 
-~ 							worldRendererIn, list, bitset, afloat);
++ 					list = BlockModelCustomizer.getRenderQuads(list, blockAccessIn, blockStateIn, blockPosIn,
++ 							enumfacing, enumworldblocklayer, 0L, renderenv);
 
-> CHANGE  8 : 9  @  8 : 9
+> CHANGE  1 : 2  @  1 : 2
 
-~ 					worldRendererIn, list1, bitset, afloat);
+~ 							worldRendererIn, list, renderenv);
 
-> INSERT  9 : 11  @  9
+> INSERT  7 : 9  @  7
 
-+ 		boolean isDeferred = DeferredStateManager.isDeferredRenderer();
-+ 		boolean isDynamicLights = isDeferred || DynamicLightsStateManager.isDynamicLightsRender();
++ 			list1 = BlockModelCustomizer.getRenderQuads(list1, blockAccessIn, blockStateIn, blockPosIn,
++ 					(EnumFacing) null, enumworldblocklayer, 0L, renderenv);
+
+> CHANGE  1 : 2  @  1 : 2
+
+~ 					worldRendererIn, list1, renderenv);
+
+> CHANGE  6 : 14  @  6 : 9
+
+~ 	private void renderModelAmbientOcclusionQuads(IBlockAccess blockAccessIn, IBlockState blockStateIn,
+~ 			BlockPos blockPosIn, WorldRenderer worldRendererIn, List<BakedQuad> listQuadsIn, RenderEnv renderenv) {
+~ 		boolean isDeferred = DeferredStateManager.isDeferredRenderer();
+~ 		boolean isDynamicLights = isDeferred || DynamicLightsStateManager.isDynamicLightsRender();
+~ 		float[] quadBounds = renderenv.getQuadBounds();
+~ 		BitSet boundsFlags = renderenv.getBoundsFlags();
+~ 		BlockModelRenderer.AmbientOcclusionFace aoFaceIn = renderenv.getAoFace();
+~ 		Block blockIn = blockStateIn.getBlock();
 
 > CHANGE  8 : 9  @  8 : 9
 
@@ -104,11 +245,13 @@
 + 	private final BlockPos blockpos5 = new BlockPos(0, 0, 0);
 + 
 
-> CHANGE  2 : 5  @  2 : 3
+> CHANGE  2 : 7  @  2 : 3
 
-~ 			List<BakedQuad> listQuadsIn, BitSet boundsFlags, float[] quadBounds) {
+~ 			List<BakedQuad> listQuadsIn, RenderEnv renderenv) {
 ~ 		boolean isDeferred = DeferredStateManager.isDeferredRenderer();
 ~ 		boolean isDynamicLights = isDeferred || DynamicLightsStateManager.isDynamicLightsRender();
+~ 		BitSet boundsFlags = renderenv.getBoundsFlags();
+~ 		float[] quadBounds = renderenv.getQuadBounds();
 
 > CHANGE  11 : 12  @  11 : 12
 
@@ -263,7 +406,11 @@
 ~ 			worldrenderer.putNormal((float) vec3i.getX(), (float) vec3i.getY(), (float) vec3i.getZ(),
 ~ 					VertexMarkerState.markId);
 
-> INSERT  9 : 16  @  9
+> CHANGE  5 : 6  @  5 : 6
+
+~ 	public static class AmbientOcclusionFace {
+
+> INSERT  3 : 10  @  3
 
 + 		private final BlockPos blockpos0 = new BlockPos(0, 0, 0);
 + 		private final BlockPos blockpos1 = new BlockPos(0, 0, 0);
diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java
index 8417dbb5..df69ea00 100644
--- a/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java
@@ -24,11 +24,23 @@
 
 ~ 			this.bakedModelStore.put((IBlockState) entry.getKey(),
 
-> CHANGE  175 : 176  @  175 : 176
+> CHANGE  157 : 158  @  157 : 158
+
+~ 						getPropertyString(linkedhashmap));
+
+> CHANGE  11 : 12  @  11 : 12
+
+~ 						getPropertyString(linkedhashmap));
+
+> CHANGE  5 : 6  @  5 : 6
 
 ~ 				String s = BlockDirt.VARIANT.getName((BlockDirt.DirtType) linkedhashmap.remove(BlockDirt.VARIANT));
 
-> CHANGE  10 : 12  @  10 : 11
+> CHANGE  4 : 5  @  4 : 5
+
+~ 				return new ModelResourceLocation(s, getPropertyString(linkedhashmap));
+
+> CHANGE  5 : 7  @  5 : 6
 
 ~ 				String s = BlockStoneSlab.VARIANT
 ~ 						.getName((BlockStoneSlab.EnumType) linkedhashmap.remove(BlockStoneSlab.VARIANT));
diff --git a/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java
index 869a2cf3..8fda3e06 100644
--- a/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java
@@ -79,7 +79,9 @@
 
 + import net.minecraft.util.Vec3i;
 
-> DELETE  2  @  2 : 9
+> CHANGE  2 : 3  @  2 : 9
+
+~ import net.optifine.Config;
 
 > CHANGE  9 : 10  @  9 : 10
 
@@ -92,10 +94,12 @@
 + 	private GameOverlayFramebuffer overlayFramebuffer;
 + 	private float eagPartialTicks = 0.0f;
 
-> INSERT  1 : 3  @  1
+> INSERT  1 : 5  @  1
 
 + 	public float currentProjMatrixFOV = 0.0f;
 + 
++ 	private boolean initializedOF = false;
++ 
 
 > DELETE  1  @  1 : 2
 
@@ -155,7 +159,11 @@
 
 ~ 				f = this.mc.isZoomKey ? this.mc.adjustedZoomValue : this.mc.gameSettings.fovSetting;
 
-> CHANGE  169 : 173  @  169 : 172
+> CHANGE  156 : 157  @  156 : 157
+
+~ 		this.farPlaneDistance = (float) (this.mc.gameSettings.renderDistanceChunks * 16 + 16);
+
+> CHANGE  12 : 16  @  12 : 15
 
 ~ 		float farPlane = this.farPlaneDistance * 2.0f * MathHelper.SQRT_2;
 ~ 		GlStateManager.gluPerspective(currentProjMatrixFOV = this.getFOVModifier(partialTicks, true),
@@ -215,8 +223,12 @@
 
 > DELETE  1  @  1 : 2
 
-> CHANGE  10 : 11  @  10 : 11
+> CHANGE  10 : 15  @  10 : 11
 
+~ 		if (!initializedOF) {
+~ 			Config.frameInitHook();
+~ 			initializedOF = true;
+~ 		}
 ~ 		boolean flag = Display.isActive() || mc.gameSettings.touchscreen;
 
 > CHANGE  1 : 2  @  1 : 2
diff --git a/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java
index 044f6faf..f205ee08 100644
--- a/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java
@@ -5,10 +5,9 @@
 # Version: 1.0
 # Author: lax1dude
 
-> CHANGE  2 : 6  @  2 : 3
+> CHANGE  2 : 5  @  2 : 3
 
-~ import com.carrotsearch.hppc.ObjectContainer;
-~ import com.carrotsearch.hppc.cursors.ObjectCursor;
+~ import java.util.List;
 ~ 
 ~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
 
@@ -16,15 +15,14 @@
 
 > CHANGE  40 : 41  @  40 : 41
 
-~ 		ObjectContainer<PotionEffect> collection = this.mc.thePlayer.getActivePotionEffects();
+~ 		List<PotionEffect> collection = this.mc.thePlayer.getActivePotionEffectsList();
 
 > INSERT  3 : 4  @  3
 
 + 			GlStateManager.enableAlpha();
 
-> CHANGE  5 : 7  @  5 : 6
+> CHANGE  5 : 6  @  5 : 6
 
-~ 			for (ObjectCursor<PotionEffect> potioneffect_ : collection) {
-~ 				PotionEffect potioneffect = potioneffect_.value;
+~ 			for (PotionEffect potioneffect : collection) {
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java b/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java
index e7704fe7..b6fd3ea5 100644
--- a/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java
@@ -16,7 +16,13 @@
 ~ 
 ~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
 
-> CHANGE  7 : 9  @  7 : 9
+> INSERT  5 : 8  @  5
+
++ import net.minecraft.util.ResourceLocation;
++ import net.optifine.Config;
++ import net.optifine.CustomItems;
+
+> CHANGE  2 : 4  @  2 : 4
 
 ~ 	private final IntObjectMap<ModelResourceLocation> simpleShapes = new IntObjectHashMap<>();
 ~ 	private final IntObjectMap<IBakedModel> simpleShapesCache = new IntObjectHashMap<>();
@@ -33,7 +39,14 @@
 
 ~ 			ItemMeshDefinition itemmeshdefinition = this.shapers.get(item);
 
-> CHANGE  17 : 18  @  17 : 18
+> INSERT  9 : 13  @  9
+
++ 		if (Config.isCustomItems()) {
++ 			ibakedmodel = CustomItems.getCustomItemModel(stack, ibakedmodel, (ResourceLocation) null, true);
++ 		}
++ 
+
+> CHANGE  8 : 9  @  8 : 9
 
 ~ 		return this.simpleShapesCache.get(this.getIndex(item, meta));
 
diff --git a/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java b/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java
index 4b05bc03..f35b6ba6 100644
--- a/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java
@@ -16,13 +16,14 @@
 ~ import net.lax1dude.eaglercraft.v1_8.Keyboard;
 ~ 
 
-> INSERT  2 : 23  @  2
+> INSERT  2 : 24  @  2
 
 + 
 + import com.google.common.collect.Lists;
 + import com.google.common.collect.Maps;
 + import com.google.common.collect.Sets;
 + 
++ import dev.redstudio.alfheim.utils.DeduplicatedLongQueue;
 + import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
 + import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
 + import net.lax1dude.eaglercraft.v1_8.minecraft.ChunkUpdateManager;
@@ -59,7 +60,9 @@
 
 + import net.minecraft.util.EnumChatFormatting;
 
-> DELETE  13  @  13 : 18
+> CHANGE  13 : 14  @  13 : 18
+
+~ import net.optifine.CustomSky;
 
 > DELETE  20  @  20 : 24
 
@@ -72,7 +75,12 @@
 ~ 	private float lastViewProjMatrixFOV = Float.MIN_VALUE;
 ~ 	private final ChunkUpdateManager renderDispatcher = new ChunkUpdateManager();
 
-> CHANGE  22 : 24  @  22 : 24
+> INSERT  17 : 19  @  17
+
++ 	private final DeduplicatedLongQueue alfheim$lightUpdatesQueue = new DeduplicatedLongQueue(8192);
++ 
+
+> CHANGE  5 : 7  @  5 : 7
 
 ~ 		EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
 ~ 		EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
@@ -615,7 +623,15 @@
 ~ 		}
 ~ 		return i;
 
-> CHANGE  92 : 93  @  92 : 102
+> CHANGE  18 : 19  @  18 : 19
+
+~ 		alfheim$processLightUpdates();
+
+> INSERT  71 : 72  @  71
+
++ 			GlStateManager.disableDepth();
+
+> CHANGE  2 : 3  @  2 : 12
 
 ~ 			GlStateManager.callList(this.glSkyList);
 
@@ -623,15 +639,42 @@
 
 ~ 							.pos((double) (f12 * 120.0F), (double) (f13 * 120.0F), (double) (f13 * 40.0F * afloat[3]))
 
-> CHANGE  42 : 43  @  42 : 52
+> INSERT  14 : 15  @  14
+
++ 			CustomSky.renderSky(this.theWorld, this.renderEngine, partialTicks);
+
+> CHANGE  26 : 28  @  26 : 27
+
+~ 			boolean b = !CustomSky.hasSkyLayers(this.theWorld);
+~ 			if (f15 > 0.0F && b) {
+
+> CHANGE  1 : 2  @  1 : 11
 
 ~ 				GlStateManager.callList(this.starGLCallList);
 
-> CHANGE  13 : 14  @  13 : 23
+> CHANGE  10 : 11  @  10 : 11
+
+~ 			if (d0 < 0.0D && b) {
+
+> CHANGE  2 : 3  @  2 : 12
 
 ~ 				GlStateManager.callList(this.glSkyList2);
 
-> CHANGE  372 : 373  @  372 : 373
+> CHANGE  35 : 42  @  35 : 39
+
+~ 			if (b) {
+~ 				GlStateManager.pushMatrix();
+~ 				GlStateManager.translate(0.0F, -((float) (d0 - 16.0D)), 0.0F);
+~ 				GlStateManager.callList(this.glSkyList2);
+~ 				GlStateManager.popMatrix();
+~ 			}
+~ 
+
+> INSERT  2 : 3  @  2
+
++ 			GlStateManager.enableDepth();
+
+> CHANGE  331 : 332  @  331 : 332
 
 ~ 		this.displayListEntitiesDirty |= this.renderDispatcher.updateChunks(finishTimeNano);
 
@@ -668,11 +711,15 @@
 
 ~ 			EaglercraftGPU.glLineWidth(2.0F);
 
-> CHANGE  240 : 241  @  240 : 241
+> CHANGE  111 : 112  @  111 : 115
+
+~ 		this.alfheim$lightUpdatesQueue.enqueue(blockpos.toLong());
+
+> CHANGE  125 : 126  @  125 : 126
 
 ~ 		EaglercraftRandom random = this.theWorld.rand;
 
-> INSERT  229 : 248  @  229
+> INSERT  229 : 263  @  229
 
 + 
 + 	public String getDebugInfoShort() {
@@ -693,5 +740,20 @@
 + 		return "" + Minecraft.getDebugFPS() + "fps | C: " + j + "/" + i + ", E: " + this.countEntitiesRendered + "+" + k
 + 				+ ", " + renderDispatcher.getDebugInfo();
 + 	}
++ 
++ 	public void alfheim$processLightUpdates() {
++ 		if (alfheim$lightUpdatesQueue.isEmpty())
++ 			return;
++ 
++ 		do {
++ 			final long longPos = alfheim$lightUpdatesQueue.dequeue();
++ 			final int x = (int) (longPos << 64 - BlockPos.X_SHIFT - BlockPos.NUM_X_BITS >> 64 - BlockPos.NUM_X_BITS);
++ 			final int y = (int) (longPos << 64 - BlockPos.Y_SHIFT - BlockPos.NUM_Y_BITS >> 64 - BlockPos.NUM_Y_BITS);
++ 			final int z = (int) (longPos << 64 - BlockPos.NUM_Z_BITS >> 64 - BlockPos.NUM_Z_BITS);
++ 			markBlocksForUpdate(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1);
++ 		} while (!alfheim$lightUpdatesQueue.isEmpty());
++ 
++ 		alfheim$lightUpdatesQueue.newDeduplicationSet();
++ 	}
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.edit.java
index f586a494..462db739 100644
--- a/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.edit.java
@@ -5,29 +5,85 @@
 # Version: 1.0
 # Author: lax1dude
 
-> INSERT  6 : 7  @  6
+> INSERT  2 : 3  @  2
+
++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
+
+> INSERT  1 : 2  @  1
+
++ import net.optifine.model.QuadBounds;
+
+> INSERT  3 : 4  @  3
 
 + 	protected final int[] vertexDataWithNormals;
 
-> INSERT  5 : 6  @  5
+> INSERT  2 : 4  @  2
 
-+ 		this.vertexDataWithNormals = null;
++ 	protected final EaglerTextureAtlasSprite sprite;
++ 	private QuadBounds quadBounds;
 
-> INSERT  4 : 11  @  4
+> CHANGE  1 : 3  @  1 : 2
+
+~ 	public BakedQuad(int[] vertexDataIn, int[] vertexDataWithNormalsIn, int tintIndexIn, EnumFacing faceIn,
+~ 			EaglerTextureAtlasSprite sprite) {
+
+> INSERT  1 : 2  @  1
 
-+ 	public BakedQuad(int[] vertexDataIn, int[] vertexDataWithNormalsIn, int tintIndexIn, EnumFacing faceIn) {
-+ 		this.vertexData = vertexDataIn;
 + 		this.vertexDataWithNormals = vertexDataWithNormalsIn;
-+ 		this.tintIndex = tintIndexIn;
-+ 		this.face = faceIn;
-+ 	}
-+ 
 
-> INSERT  4 : 8  @  4
+> INSERT  2 : 3  @  2
+
++ 		this.sprite = sprite;
+
+> INSERT  6 : 10  @  6
 
 + 	public int[] getVertexDataWithNormals() {
 + 		return this.vertexDataWithNormals;
 + 	}
 + 
 
+> INSERT  11 : 52  @  11
+
++ 
++ 	public EaglerTextureAtlasSprite getSprite() {
++ 		return sprite;
++ 	}
++ 
++ 	public QuadBounds getQuadBounds() {
++ 		if (this.quadBounds == null) {
++ 			this.quadBounds = new QuadBounds(this.getVertexData());
++ 		}
++ 
++ 		return this.quadBounds;
++ 	}
++ 
++ 	public float getMidX() {
++ 		QuadBounds quadbounds = this.getQuadBounds();
++ 		return (quadbounds.getMaxX() + quadbounds.getMinX()) / 2.0F;
++ 	}
++ 
++ 	public double getMidY() {
++ 		QuadBounds quadbounds = this.getQuadBounds();
++ 		return (double) ((quadbounds.getMaxY() + quadbounds.getMinY()) / 2.0F);
++ 	}
++ 
++ 	public double getMidZ() {
++ 		QuadBounds quadbounds = this.getQuadBounds();
++ 		return (double) ((quadbounds.getMaxZ() + quadbounds.getMinZ()) / 2.0F);
++ 	}
++ 
++ 	public boolean isFaceQuad() {
++ 		QuadBounds quadbounds = this.getQuadBounds();
++ 		return quadbounds.isFaceQuad(this.face);
++ 	}
++ 
++ 	public boolean isFullQuad() {
++ 		QuadBounds quadbounds = this.getQuadBounds();
++ 		return quadbounds.isFullQuad(this.face);
++ 	}
++ 
++ 	public boolean isFullFaceQuad() {
++ 		return this.isFullQuad() && this.isFaceQuad();
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java
index 661afea8..481a8be2 100644
--- a/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java
@@ -12,24 +12,24 @@
 + import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
 + 
 
-> CHANGE  1 : 2  @  1 : 2
+> DELETE  1  @  1 : 2
 
-~ 	private final EaglerTextureAtlasSprite texture;
-
-> CHANGE  1 : 5  @  1 : 4
+> CHANGE  1 : 5  @  1 : 5
 
 ~ 	public BreakingFour(BakedQuad parBakedQuad, EaglerTextureAtlasSprite textureIn) {
 ~ 		super(Arrays.copyOf(parBakedQuad.getVertexData(), parBakedQuad.getVertexData().length),
 ~ 				Arrays.copyOf(parBakedQuad.getVertexDataWithNormals(), parBakedQuad.getVertexDataWithNormals().length),
-~ 				parBakedQuad.tintIndex, parBakedQuad.face);
+~ 				parBakedQuad.tintIndex, parBakedQuad.face, textureIn);
 
-> INSERT  46 : 52  @  46
+> CHANGE  43 : 51  @  43 : 45
 
-+ 		if (this.vertexDataWithNormals != null) {
-+ 			int i2 = 8 * parInt1;
-+ 			this.vertexDataWithNormals[i2 + 4] = this.vertexData[i + 4];
-+ 			this.vertexDataWithNormals[i2 + 4 + 1] = this.vertexData[i + 4 + 1];
-+ 
-+ 		}
+~ 		this.vertexData[i + 4] = Float.floatToRawIntBits(sprite.getInterpolatedU((double) f3));
+~ 		this.vertexData[i + 4 + 1] = Float.floatToRawIntBits(sprite.getInterpolatedV((double) f4));
+~ 		if (this.vertexDataWithNormals != null) {
+~ 			int i2 = 8 * parInt1;
+~ 			this.vertexDataWithNormals[i2 + 4] = this.vertexData[i + 4];
+~ 			this.vertexDataWithNormals[i2 + 4 + 1] = this.vertexData[i + 4 + 1];
+~ 
+~ 		}
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java
index 026f08d2..aff26eed 100644
--- a/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java
@@ -47,7 +47,7 @@
 ~ 		}
 ~ 		stride = 7;
 ~ 
-~ 		return new BakedQuad(aint, aint2, face.tintIndex, enumfacing);
+~ 		return new BakedQuad(aint, aint2, face.tintIndex, enumfacing, sprite);
 
 > CHANGE  2 : 3  @  2 : 3
 
diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java
index f68c81c5..aab3c777 100644
--- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java
@@ -17,4 +17,8 @@
 
 > DELETE  2  @  2 : 4
 
+> CHANGE  3 : 4  @  3 : 4
+
+~ 	public Map<Block, IStateMapper> blockStateMap = Maps.newIdentityHashMap();
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java
index 4e68ba62..6972c301 100644
--- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java
@@ -7,4 +7,8 @@
 
 > DELETE  4  @  4 : 5
 
+> CHANGE  7 : 8  @  7 : 8
+
+~ 				getPropertyString(iblockstate.getProperties()));
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java
index a7247f30..a9792d49 100644
--- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java
@@ -24,4 +24,8 @@
 
 ~ 			s = this.name.getName(linkedhashmap.remove(this.name));
 
+> CHANGE  10 : 11  @  10 : 11
+
+~ 		return new ModelResourceLocation(s, getPropertyString(linkedhashmap));
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java
index 82d5311a..fcbd1922 100644
--- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java
@@ -15,4 +15,8 @@
 
 > DELETE  3  @  3 : 4
 
+> CHANGE  5 : 6  @  5 : 6
+
+~ 	public static String getPropertyString(Map<IProperty, Comparable> parMap) {
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java
index 88a65bc3..e8048a44 100644
--- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java
@@ -27,104 +27,141 @@
 
 + import net.minecraft.util.EnumWorldBlockLayer;
 
-> CHANGE  55 : 58  @  55 : 56
+> INSERT  3 : 5  @  3
+
++ import net.optifine.Config;
++ import net.optifine.CustomItems;
+
+> INSERT  8 : 9  @  8
+
++ 	private ModelResourceLocation modelLocation = null;
+
+> CHANGE  35 : 36  @  35 : 36
+
+~ 	public void renderModel(IBakedModel model, int color) {
+
+> CHANGE  8 : 11  @  8 : 9
 
 ~ 		EnumFacing[] facings = EnumFacing._VALUES;
 ~ 		for (int i = 0; i < facings.length; ++i) {
 ~ 			EnumFacing enumfacing = facings[i];
 
-> INSERT  7 : 11  @  7
+> CHANGE  7 : 12  @  7 : 8
 
-+ 	public static float renderPosX = 0.0f;
-+ 	public static float renderPosY = 0.0f;
-+ 	public static float renderPosZ = 0.0f;
+~ 	public static float renderPosX = 0.0f;
+~ 	public static float renderPosY = 0.0f;
+~ 	public static float renderPosZ = 0.0f;
+~ 
+~ 	public void renderItem(ItemStack stack, IBakedModel model_) {
+
+> CHANGE  3 : 4  @  3 : 4
+
+~ 			if (model_.isBuiltInRenderer()) {
+
+> CHANGE  7 : 9  @  7 : 10
+
+~ 				if (Config.isCustomItems()) {
+~ 					model_ = CustomItems.getCustomItemModel(stack, model_, this.modelLocation, false);
+
+> INSERT  1 : 74  @  1
+
++ 				final IBakedModel model = model_;
 + 
++ 				if (DeferredStateManager.isInDeferredPass() && isTransparentItem(stack)) {
++ 					if (DeferredStateManager.forwardCallbackHandler != null) {
++ 						final Matrix4f mat = new Matrix4f(GlStateManager.getModelViewReference());
++ 						final float lx = GlStateManager.getTexCoordX(1), ly = GlStateManager.getTexCoordY(1);
++ 						DeferredStateManager.forwardCallbackHandler.push(new ShadersRenderPassFuture(renderPosX,
++ 								renderPosY, renderPosZ, EaglerDeferredPipeline.instance.getPartialTicks()) {
++ 							@Override
++ 							public void draw(PassType pass) {
++ 								if (pass == PassType.MAIN) {
++ 									DeferredStateManager.reportForwardRenderObjectPosition2(x, y, z);
++ 								}
++ 								EntityRenderer.enableLightmapStatic();
++ 								GlStateManager.pushMatrix();
++ 								GlStateManager.loadMatrix(mat);
++ 								GlStateManager.texCoords2DDirect(1, lx, ly);
++ 								Minecraft.getMinecraft().getTextureManager()
++ 										.bindTexture(TextureMap.locationBlocksTexture);
++ 								RenderItem.this.renderModel(model, stack);
++ 								if (pass != PassType.SHADOW && stack.hasEffect()) {
++ 									GlStateManager.color(1.5F, 0.5F, 1.5F, 1.0F);
++ 									DeferredStateManager.setDefaultMaterialConstants();
++ 									DeferredStateManager.setRoughnessConstant(0.05f);
++ 									DeferredStateManager.setMetalnessConstant(0.01f);
++ 									GlStateManager.blendFunc(768, 1);
++ 									renderEffect(model, stack);
++ 									DeferredStateManager.setHDRTranslucentPassBlendFunc();
++ 								}
++ 								GlStateManager.popMatrix();
++ 								EntityRenderer.disableLightmapStatic();
++ 								GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
++ 							}
++ 						});
++ 					}
++ 				} else {
++ 					this.renderModel(model, stack);
++ 					if (stack.hasEffect()) {
++ 						if (DeferredStateManager.isInDeferredPass()) {
++ 							if (DeferredStateManager.forwardCallbackHandler != null
++ 									&& !DeferredStateManager.isEnableShadowRender()) {
++ 								final Matrix4f mat = new Matrix4f(GlStateManager.getModelViewReference());
++ 								final float lx = GlStateManager.getTexCoordX(1), ly = GlStateManager.getTexCoordY(1);
++ 								DeferredStateManager.forwardCallbackHandler.push(new ShadersRenderPassFuture(renderPosX,
++ 										renderPosY, renderPosZ, EaglerDeferredPipeline.instance.getPartialTicks()) {
++ 									@Override
++ 									public void draw(PassType pass) {
++ 										if (pass == PassType.MAIN) {
++ 											DeferredStateManager.reportForwardRenderObjectPosition2(x, y, z);
++ 										}
++ 										EntityRenderer.enableLightmapStatic();
++ 										GlStateManager.color(1.5F, 0.5F, 1.5F, 1.0F);
++ 										DeferredStateManager.setDefaultMaterialConstants();
++ 										DeferredStateManager.setRoughnessConstant(0.05f);
++ 										DeferredStateManager.setMetalnessConstant(0.01f);
++ 										GlStateManager.pushMatrix();
++ 										GlStateManager.loadMatrix(mat);
++ 										GlStateManager.texCoords2DDirect(1, lx, ly);
++ 										GlStateManager.tryBlendFuncSeparate(GL_ONE, GL_ONE, GL_ZERO, GL_ONE);
++ 										renderEffect(model, stack);
++ 										DeferredStateManager.setHDRTranslucentPassBlendFunc();
++ 										GlStateManager.popMatrix();
++ 										EntityRenderer.disableLightmapStatic();
++ 										GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
++ 									}
++ 								});
++ 							}
++ 						} else {
++ 							GlStateManager.blendFunc(768, 1);
++ 							this.renderEffect(model, stack);
++ 						}
++ 					}
++ 				}
 
-> CHANGE  12 : 82  @  12 : 15
+> CHANGE  6 : 15  @  6 : 7
 
-~ 				if (DeferredStateManager.isInDeferredPass() && isTransparentItem(stack)) {
-~ 					if (DeferredStateManager.forwardCallbackHandler != null) {
-~ 						final Matrix4f mat = new Matrix4f(GlStateManager.getModelViewReference());
-~ 						final float lx = GlStateManager.getTexCoordX(1), ly = GlStateManager.getTexCoordY(1);
-~ 						DeferredStateManager.forwardCallbackHandler.push(new ShadersRenderPassFuture(renderPosX,
-~ 								renderPosY, renderPosZ, EaglerDeferredPipeline.instance.getPartialTicks()) {
-~ 							@Override
-~ 							public void draw(PassType pass) {
-~ 								if (pass == PassType.MAIN) {
-~ 									DeferredStateManager.reportForwardRenderObjectPosition2(x, y, z);
-~ 								}
-~ 								EntityRenderer.enableLightmapStatic();
-~ 								GlStateManager.pushMatrix();
-~ 								GlStateManager.loadMatrix(mat);
-~ 								GlStateManager.texCoords2DDirect(1, lx, ly);
-~ 								Minecraft.getMinecraft().getTextureManager()
-~ 										.bindTexture(TextureMap.locationBlocksTexture);
-~ 								RenderItem.this.renderModel(model, stack);
-~ 								if (pass != PassType.SHADOW && stack.hasEffect()) {
-~ 									GlStateManager.color(1.5F, 0.5F, 1.5F, 1.0F);
-~ 									DeferredStateManager.setDefaultMaterialConstants();
-~ 									DeferredStateManager.setRoughnessConstant(0.05f);
-~ 									DeferredStateManager.setMetalnessConstant(0.01f);
-~ 									GlStateManager.blendFunc(768, 1);
-~ 									renderEffect(model);
-~ 									DeferredStateManager.setHDRTranslucentPassBlendFunc();
-~ 								}
-~ 								GlStateManager.popMatrix();
-~ 								EntityRenderer.disableLightmapStatic();
-~ 								GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
-~ 							}
-~ 						});
-~ 					}
-~ 				} else {
-~ 					this.renderModel(model, stack);
-~ 					if (stack.hasEffect()) {
-~ 						if (DeferredStateManager.isInDeferredPass()) {
-~ 							if (DeferredStateManager.forwardCallbackHandler != null
-~ 									&& !DeferredStateManager.isEnableShadowRender()) {
-~ 								final Matrix4f mat = new Matrix4f(GlStateManager.getModelViewReference());
-~ 								final float lx = GlStateManager.getTexCoordX(1), ly = GlStateManager.getTexCoordY(1);
-~ 								DeferredStateManager.forwardCallbackHandler.push(new ShadersRenderPassFuture(renderPosX,
-~ 										renderPosY, renderPosZ, EaglerDeferredPipeline.instance.getPartialTicks()) {
-~ 									@Override
-~ 									public void draw(PassType pass) {
-~ 										if (pass == PassType.MAIN) {
-~ 											DeferredStateManager.reportForwardRenderObjectPosition2(x, y, z);
-~ 										}
-~ 										EntityRenderer.enableLightmapStatic();
-~ 										GlStateManager.color(1.5F, 0.5F, 1.5F, 1.0F);
-~ 										DeferredStateManager.setDefaultMaterialConstants();
-~ 										DeferredStateManager.setRoughnessConstant(0.05f);
-~ 										DeferredStateManager.setMetalnessConstant(0.01f);
-~ 										GlStateManager.pushMatrix();
-~ 										GlStateManager.loadMatrix(mat);
-~ 										GlStateManager.texCoords2DDirect(1, lx, ly);
-~ 										GlStateManager.tryBlendFuncSeparate(GL_ONE, GL_ONE, GL_ZERO, GL_ONE);
-~ 										renderEffect(model);
-~ 										DeferredStateManager.setHDRTranslucentPassBlendFunc();
-~ 										GlStateManager.popMatrix();
-~ 										EntityRenderer.disableLightmapStatic();
-~ 										GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
-~ 									}
-~ 								});
-~ 							}
-~ 						} else {
-~ 							GlStateManager.blendFunc(768, 1);
-~ 							this.renderEffect(model);
-~ 						}
-~ 					}
+~ 	private static boolean isTransparentItem(ItemStack stack) {
+~ 		Item itm = stack.getItem();
+~ 		return itm instanceof ItemBlock
+~ 				&& ((ItemBlock) itm).getBlock().getBlockLayer() == EnumWorldBlockLayer.TRANSLUCENT;
+~ 	}
+~ 
+~ 	private void renderEffect(IBakedModel model, ItemStack stack) {
+~ 		if (Config.isCustomItems() && (CustomItems.renderCustomEffect(this, stack, model) || !CustomItems.isUseGlint()))
+~ 			return;
 
-> INSERT  7 : 13  @  7
+> DELETE  3  @  3 : 4
 
-+ 	private static boolean isTransparentItem(ItemStack stack) {
-+ 		Item itm = stack.getItem();
-+ 		return itm instanceof ItemBlock
-+ 				&& ((ItemBlock) itm).getBlock().getBlockLayer() == EnumWorldBlockLayer.TRANSLUCENT;
-+ 	}
-+ 
+> INSERT  104 : 105  @  104
 
-> DELETE  4  @  4 : 5
++ 					this.modelLocation = modelresourcelocation;
 
-> INSERT  123 : 124  @  123
+> INSERT  4 : 5  @  4
+
++ 			this.modelLocation = null;
+
+> INSERT  15 : 16  @  15
 
 + 		boolean flag = DeferredStateManager.isEnableShadowRender();
 
diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java
index 9c39ad06..639c41c0 100644
--- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java
@@ -25,13 +25,24 @@
 
 > DELETE  1  @  1 : 2
 
-> CHANGE  47 : 48  @  47 : 48
+> INSERT  4 : 6  @  4
+
++ import net.optifine.Config;
++ import net.optifine.CustomItems;
+
+> CHANGE  43 : 44  @  43 : 44
 
 ~ 			this.func_177179_a((T) modelbase, parInt1);
 
-> INSERT  2 : 3  @  2
+> CHANGE  1 : 8  @  1 : 2
 
-+ 			DeferredStateManager.setDefaultMaterialConstants();
+~ 
+~ 			if (!Config.isCustomItems()
+~ 					|| !CustomItems.bindCustomArmorTexture(itemstack, flag ? 2 : 1, (String) null)) {
+~ 				this.renderer.bindTexture(this.getArmorResource(itemarmor, flag));
+~ 			}
+~ 
+~ 			DeferredStateManager.setDefaultMaterialConstants();
 
 > INSERT  1 : 18  @  1
 
@@ -53,11 +64,18 @@
 + 			}
 + 			switch (itemarmor.getArmorMaterial()) {
 
-> INSERT  14 : 15  @  14
+> CHANGE  7 : 11  @  7 : 8
+
+~ 				if (!Config.isCustomItems()
+~ 						|| !CustomItems.bindCustomArmorTexture(itemstack, flag ? 2 : 1, "overlay")) {
+~ 					this.renderer.bindTexture(this.getArmorResource(itemarmor, flag, "overlay"));
+~ 				}
+
+> INSERT  6 : 7  @  6
 
 + 				DeferredStateManager.setDefaultMaterialConstants();
 
-> CHANGE  2 : 41  @  2 : 3
+> CHANGE  2 : 43  @  2 : 4
 
 ~ 					if (DeferredStateManager.isInDeferredPass()) {
 ~ 						if (!DeferredStateManager.isEnableShadowRender()
@@ -86,7 +104,8 @@
 ~ 									modelbase.setLivingAnimations(entitylivingbaseIn, armorSlot, parFloat2, parFloat3);
 ~ 									LayerArmorBase.this.func_177179_a((T) modelbase, parInt1);
 ~ 									LayerArmorBase.this.func_177183_a(entitylivingbaseIn, (T) modelbase, armorSlot,
-~ 											parFloat2, parFloat3, parFloat4, parFloat5, parFloat6, parFloat7);
+~ 											parFloat2, parFloat3, parFloat4, parFloat5, parFloat6, parFloat7,
+~ 											itemstack);
 ~ 									DeferredStateManager.setHDRTranslucentPassBlendFunc();
 ~ 									GlStateManager.enableBlend();
 ~ 									GlStateManager.popMatrix();
@@ -98,8 +117,17 @@
 ~ 						break;
 ~ 					}
 ~ 					this.func_177183_a(entitylivingbaseIn, (T) modelbase, armorSlot, parFloat2, parFloat3, parFloat4,
+~ 							parFloat5, parFloat6, parFloat7, itemstack);
 
-> CHANGE  27 : 31  @  27 : 28
+> CHANGE  19 : 24  @  19 : 20
+
+~ 			float parFloat3, float parFloat4, float parFloat5, float parFloat6, float parFloat7, ItemStack itemstack) {
+~ 		if (Config.isCustomItems() && CustomItems.renderCustomArmorEffect(entitylivingbaseIn, itemstack, modelbaseIn,
+~ 				parFloat1, parFloat2, parFloat3, parFloat4, parFloat5, parFloat6, parFloat7)) {
+~ 			return;
+~ 		}
+
+> CHANGE  6 : 10  @  6 : 7
 
 ~ 		boolean d = !DeferredStateManager.isInDeferredPass();
 ~ 		if (d) {
diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java
index fabebcdc..0f56e25b 100644
--- a/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java
@@ -16,16 +16,22 @@
 
 ~ public class TextureClock extends EaglerTextureAtlasSprite {
 
-> CHANGE  7 : 8  @  7 : 8
+> CHANGE  41 : 48  @  41 : 43
 
-~ 	public void updateAnimation(IFramebufferGL[] copyColorFramebuffer) {
+~ 				currentAnimUpdater = (mapWidth, mapHeight, mapLevel) -> {
+~ 					animationCache.copyFrameToTex2D(this.frameCounter, mapLevel, this.originX >> mapLevel,
+~ 							this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+~ 							mapHeight);
+~ 				};
+~ 			} else {
+~ 				currentAnimUpdater = null;
 
-> CHANGE  33 : 35  @  33 : 35
+> INSERT  2 : 4  @  2
 
-~ 				animationCache.copyFrameLevelsToTex2D(this.frameCounter, this.originX, this.originY, this.width,
-~ 						this.height, copyColorFramebuffer);
++ 		} else {
++ 			currentAnimUpdater = null;
 
-> INSERT  4 : 5  @  4
+> INSERT  2 : 3  @  2
 
 + 
 
diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java
index 0b44a73a..ff9923fa 100644
--- a/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java
@@ -16,28 +16,22 @@
 
 ~ public class TextureCompass extends EaglerTextureAtlasSprite {
 
-> CHANGE  9 : 10  @  9 : 10
+> CHANGE  62 : 69  @  62 : 64
 
-~ 	public void updateAnimation(IFramebufferGL[] copyColorFramebuffer) {
+~ 				currentAnimUpdater = (mapWidth, mapHeight, mapLevel) -> {
+~ 					animationCache.copyFrameToTex2D(this.frameCounter, mapLevel, this.originX >> mapLevel,
+~ 							this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+~ 							mapHeight);
+~ 				};
+~ 			} else {
+~ 				currentAnimUpdater = null;
 
-> CHANGE  3 : 4  @  3 : 4
+> INSERT  2 : 4  @  2
 
-~ 					(double) minecraft.thePlayer.rotationYaw, false, false, copyColorFramebuffer);
++ 		} else {
++ 			currentAnimUpdater = null;
 
-> CHANGE  1 : 2  @  1 : 2
-
-~ 			this.updateCompass((World) null, 0.0D, 0.0D, 0.0D, true, false, copyColorFramebuffer);
-
-> CHANGE  5 : 6  @  5 : 6
-
-~ 			boolean parFlag2, IFramebufferGL[] copyColorFramebuffer) {
-
-> CHANGE  40 : 42  @  40 : 42
-
-~ 				animationCache.copyFrameLevelsToTex2D(this.frameCounter, this.originX, this.originY, this.width,
-~ 						this.height, copyColorFramebuffer);
-
-> INSERT  4 : 5  @  4
+> INSERT  2 : 3  @  2
 
 + 
 
diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java
index 11758fce..9003e766 100644
--- a/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java
+++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java
@@ -34,7 +34,12 @@
 
 > DELETE  2  @  2 : 8
 
-> DELETE  9  @  9 : 11
+> CHANGE  9 : 13  @  9 : 11
+
+~ import net.optifine.BetterGrass;
+~ import net.optifine.ConnectedTextures;
+~ import net.optifine.CustomItems;
+~ import net.optifine.util.CounterInt;
 
 > INSERT  1 : 3  @  1
 
@@ -47,7 +52,7 @@
 ~ 	private final Map<String, EaglerTextureAtlasSprite> mapRegisteredSprites;
 ~ 	private final Map<String, EaglerTextureAtlasSprite> mapUploadedSprites;
 
-> CHANGE  3 : 11  @  3 : 4
+> CHANGE  3 : 12  @  3 : 4
 
 ~ 	private final EaglerTextureAtlasSprite missingImage;
 ~ 	private final EaglerTextureAtlasSpritePBR missingImagePBR;
@@ -57,6 +62,7 @@
 ~ 	public int eaglerPBRMaterialTexture = -1;
 ~ 	private boolean hasAllocatedEaglerPBRMaterialTexture = false;
 ~ 	private boolean isGLES2 = false;
+~ 	private CounterInt counterIndexInMap;
 
 > INSERT  1 : 7  @  1
 
@@ -72,12 +78,15 @@
 ~ 		this.missingImage = new EaglerTextureAtlasSprite("missingno");
 ~ 		this.missingImagePBR = new EaglerTextureAtlasSpritePBR("missingno");
 
-> INSERT  2 : 3  @  2
+> INSERT  2 : 4  @  2
 
 + 		this.isGLES2 = EaglercraftGPU.checkOpenGLESVersion() == 200;
++ 		this.counterIndexInMap = new CounterInt(0);
 
-> INSERT  9 : 25  @  9
+> INSERT  9 : 28  @  9
 
++ 		int idx = this.counterIndexInMap.nextValue();
++ 		this.missingImage.setIndexInMap(idx);
 + 		this.missingImagePBR.setIconWidth(16);
 + 		this.missingImagePBR.setIconHeight(16);
 + 		int[][][] aint2 = new int[3][this.mipmapLevels + 1][];
@@ -94,6 +103,7 @@
 + 		aint2[2][0] = missingMaterial;
 + 		this.missingImagePBR.setFramesTextureDataPBR(new List[] { Lists.newArrayList(new int[][][] { aint2[0] }),
 + 				Lists.newArrayList(new int[][][] { aint2[1] }), Lists.newArrayList(new int[][][] { aint2[2] }) });
++ 		this.missingImagePBR.setIndexInMap(idx);
 
 > DELETE  6  @  6 : 7
 
@@ -101,7 +111,11 @@
 
 + 		destroyAnimationCaches();
 
-> INSERT  7 : 27  @  7
+> INSERT  1 : 2  @  1
+
++ 		this.counterIndexInMap.reset();
+
+> INSERT  6 : 26  @  6
 
 + 	public void deleteGlTexture() {
 + 		super.deleteGlTexture();
@@ -124,7 +138,13 @@
 + 	}
 + 
 
-> INSERT  8 : 44  @  8
+> INSERT  1 : 4  @  1
+
++ 		ConnectedTextures.updateIcons(this);
++ 		CustomItems.updateIcons(this);
++ 		BetterGrass.updateIcons(this);
+
+> INSERT  7 : 43  @  7
 
 + 		if (copyColorFramebuffer != null) {
 + 			for (int l = 0; l < copyColorFramebuffer.length; ++l) {
@@ -163,11 +183,12 @@
 + 		}
 + 
 
-> CHANGE  1 : 2  @  1 : 2
+> CHANGE  1 : 3  @  1 : 2
 
 ~ 			EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) entry.getValue();
+~ 			textureatlassprite.updateIndexInMap(this.counterIndexInMap);
 
-> INSERT  3 : 105  @  3
+> INSERT  3 : 106  @  3
 
 + 			if (isEaglerPBRMode) {
 + 				try {
@@ -188,7 +209,7 @@
 + 					}
 + 					if (abufferedimageMaterial[0] == null) {
 + 						abufferedimageMaterial[0] = PBRTextureMapUtils.generateMaterialTextureFor(
-+ 								((EaglerTextureAtlasSprite) (entry.getValue())).getIconName());
++ 								textureatlassprite.getIconName(), textureatlassprite.optifineBaseTextureName);
 + 						dontAnimateMaterial = true;
 + 					}
 + 					PBRTextureMapUtils.unifySizes(0, abufferedimageColor, abufferedimageNormal, abufferedimageMaterial);
@@ -224,7 +245,8 @@
 + 									}
 + 									if (abufferedimageMaterial[i2] == null) {
 + 										abufferedimageMaterial[i2] = PBRTextureMapUtils.generateMaterialTextureFor(
-+ 												((EaglerTextureAtlasSprite) (entry.getValue())).getIconName());
++ 												textureatlassprite.getIconName(),
++ 												textureatlassprite.optifineBaseTextureName);
 + 									}
 + 									PBRTextureMapUtils.unifySizes(i2, abufferedimageColor, abufferedimageNormal,
 + 											abufferedimageMaterial);
@@ -382,13 +404,30 @@
 
 + 		_wglBindFramebuffer(_GL_FRAMEBUFFER, null);
 
-> CHANGE  5 : 7  @  5 : 7
+> CHANGE  3 : 12  @  3 : 8
 
-~ 						HString.format("%s/%s%s", new Object[] { this.basePath, location.getResourcePath(), ".png" }))
-~ 				: new ResourceLocation(location.getResourceDomain(), HString.format("%s/mipmaps/%s.%d%s",
+~ 		return isAbsoluteLocation(location)
+~ 				? new ResourceLocation(location.getResourceDomain(), location.getResourcePath() + ".png")
+~ 				: (parInt1 == 0
+~ 						? new ResourceLocation(location.getResourceDomain(),
+~ 								HString.format("%s/%s%s",
+~ 										new Object[] { this.basePath, location.getResourcePath(), ".png" }))
+~ 						: new ResourceLocation(location.getResourceDomain(),
+~ 								HString.format("%s/mipmaps/%s.%d%s", new Object[] { this.basePath,
+~ 										location.getResourcePath(), Integer.valueOf(parInt1), ".png" })));
 
-> CHANGE  3 : 5  @  3 : 5
+> CHANGE  2 : 14  @  2 : 4
 
+~ 	private boolean isAbsoluteLocation(ResourceLocation p_isAbsoluteLocation_1_) {
+~ 		String s = p_isAbsoluteLocation_1_.getResourcePath();
+~ 		return this.isAbsoluteLocationPath(s);
+~ 	}
+~ 
+~ 	private boolean isAbsoluteLocationPath(String p_isAbsoluteLocationPath_1_) {
+~ 		String s = p_isAbsoluteLocationPath_1_.toLowerCase();
+~ 		return s.startsWith("mcpatcher/") || s.startsWith("optifine/");
+~ 	}
+~ 
 ~ 	public EaglerTextureAtlasSprite getAtlasSprite(String iconName) {
 ~ 		EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.mapUploadedSprites.get(iconName);
 
@@ -396,12 +435,26 @@
 
 ~ 			textureatlassprite = isEaglerPBRMode ? missingImagePBR : missingImage;
 
-> CHANGE  6 : 14  @  6 : 7
+> CHANGE  6 : 28  @  6 : 7
 
 ~ 		if (isEaglerPBRMode) {
-~ 			for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) {
-~ 				this.listAnimatedSprites.get(i).updateAnimationPBR(copyColorFramebuffer, copyMaterialFramebuffer,
-~ 						height);
+~ 			for (int j = 0, l = this.listAnimatedSprites.size(); j < l; ++j) {
+~ 				this.listAnimatedSprites.get(j).updateAnimationPBR();
+~ 			}
+~ 			for (int i = 0; i < copyColorFramebuffer.length; ++i) {
+~ 				int w = width >> i;
+~ 				int h = height >> i;
+~ 				_wglBindFramebuffer(_GL_FRAMEBUFFER, copyColorFramebuffer[i]);
+~ 				GlStateManager.viewport(0, 0, w, h);
+~ 				for (int j = 0, l = this.listAnimatedSprites.size(); j < l; ++j) {
+~ 					this.listAnimatedSprites.get(j).copyAnimationFramePBR(0, w, h, i);
+~ 				}
+~ 				_wglBindFramebuffer(_GL_FRAMEBUFFER, copyMaterialFramebuffer[i]);
+~ 				h <<= 1;
+~ 				GlStateManager.viewport(0, 0, w, h);
+~ 				for (int j = 0, l = this.listAnimatedSprites.size(); j < l; ++j) {
+~ 					this.listAnimatedSprites.get(j).copyAnimationFramePBR(1, w, h, i);
+~ 				}
 ~ 			}
 ~ 			_wglBindFramebuffer(_GL_FRAMEBUFFER, null);
 ~ 			return;
@@ -409,14 +462,24 @@
 
 > CHANGE  1 : 3  @  1 : 3
 
-~ 		for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) {
-~ 			this.listAnimatedSprites.get(i).updateAnimation(copyColorFramebuffer);
+~ 		for (int j = 0, l = this.listAnimatedSprites.size(); j < l; ++j) {
+~ 			this.listAnimatedSprites.get(j).updateAnimation();
 
-> INSERT  2 : 3  @  2
+> INSERT  2 : 13  @  2
 
++ 		for (int i = 0; i < copyColorFramebuffer.length; ++i) {
++ 			int w = width >> i;
++ 			int h = height >> i;
++ 			_wglBindFramebuffer(_GL_FRAMEBUFFER, copyColorFramebuffer[i]);
++ 			GlStateManager.viewport(0, 0, w, h);
++ 			for (int j = 0, l = this.listAnimatedSprites.size(); j < l; ++j) {
++ 				this.listAnimatedSprites.get(j).copyAnimationFrame(w, h, i);
++ 			}
++ 		}
++ 
 + 		_wglBindFramebuffer(_GL_FRAMEBUFFER, null);
 
-> CHANGE  2 : 9  @  2 : 3
+> CHANGE  2 : 13  @  2 : 3
 
 ~ 	private void destroyAnimationCaches() {
 ~ 		for (int i = 0, l = this.listAnimatedSprites.size(); i < l; ++i) {
@@ -425,21 +488,30 @@
 ~ 	}
 ~ 
 ~ 	public EaglerTextureAtlasSprite registerSprite(ResourceLocation location) {
+~ 		return registerSprite(location, null);
+~ 	}
+~ 
+~ 	public EaglerTextureAtlasSprite registerSprite(ResourceLocation location, String locationOptifineBase) {
 
 > CHANGE  3 : 5  @  3 : 4
 
 ~ 			EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.mapRegisteredSprites
-~ 					.get(location);
+~ 					.get(location.toString());
 
-> CHANGE  1 : 6  @  1 : 2
+> CHANGE  1 : 7  @  1 : 2
 
 ~ 				if (isEaglerPBRMode) {
 ~ 					textureatlassprite = EaglerTextureAtlasSpritePBR.makeAtlasSprite(location);
 ~ 				} else {
 ~ 					textureatlassprite = EaglerTextureAtlasSprite.makeAtlasSprite(location);
 ~ 				}
+~ 				textureatlassprite.optifineBaseTextureName = locationOptifineBase;
 
-> CHANGE  12 : 18  @  12 : 13
+> INSERT  1 : 2  @  1
+
++ 				textureatlassprite.updateIndexInMap(this.counterIndexInMap);
+
+> CHANGE  11 : 17  @  11 : 12
 
 ~ 		if (!isGLES2) {
 ~ 			this.mipmapLevels = mipmapLevelsIn;
@@ -453,7 +525,7 @@
 ~ 	public EaglerTextureAtlasSprite getMissingSprite() {
 ~ 		return isEaglerPBRMode ? missingImagePBR : missingImage;
 
-> INSERT  1 : 27  @  1
+> INSERT  1 : 36  @  1
 
 + 
 + 	public int getWidth() {
@@ -481,5 +553,14 @@
 + 			}
 + 		}
 + 	}
++ 
++ 	public EaglerTextureAtlasSprite getSpriteSafe(String iconName) {
++ 		ResourceLocation resourcelocation = new ResourceLocation(iconName);
++ 		return this.mapRegisteredSprites.get(resourcelocation.toString());
++ 	}
++ 
++ 	public int getCountRegisteredSprites() {
++ 		return this.counterIndexInMap.getValue();
++ 	}
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java
index c4aad381..d8eb1499 100644
--- a/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java
+++ b/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java
@@ -7,7 +7,7 @@
 
 > DELETE  2  @  2 : 9
 
-> CHANGE  2 : 13  @  2 : 3
+> CHANGE  2 : 14  @  2 : 3
 
 ~ import java.nio.charset.StandardCharsets;
 ~ 
@@ -19,15 +19,17 @@
 ~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
 ~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
 ~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack;
+~ import net.lax1dude.eaglercraft.v1_8.minecraft.ResourceIndex;
 ~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
 
 > DELETE  1  @  1 : 2
 
 > DELETE  3  @  3 : 6
 
-> CHANGE  3 : 4  @  3 : 4
+> CHANGE  3 : 5  @  3 : 4
 
 ~ 	protected final String resourcePackFile;
+~ 	protected ResourceIndex resourceIndex;
 
 > CHANGE  1 : 2  @  1 : 2
 
@@ -50,11 +52,11 @@
 ~ 			throw e;
 ~ 		}
 
-> CHANGE  4 : 5  @  4 : 6
+> CHANGE  4 : 5  @  4 : 7
 
 ~ 		JSONObject jsonobject = null;
 
-> CHANGE  2 : 5  @  2 : 6
+> CHANGE  1 : 4  @  1 : 5
 
 ~ 			jsonobject = new JSONObject(IOUtils.inputStreamToString(parInputStream, StandardCharsets.UTF_8));
 ~ 		} catch (RuntimeException | IOException runtimeexception) {
@@ -72,4 +74,11 @@
 
 ~ 		return this.resourcePackFile;
 
+> INSERT  1 : 5  @  1
+
++ 
++ 	public ResourceIndex getEaglerFileIndex() {
++ 		return this.resourceIndex;
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java
index 2c1f9b9f..e782916e 100644
--- a/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java
+++ b/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java
@@ -7,25 +7,44 @@
 
 > DELETE  2  @  2 : 6
 
-> DELETE  3  @  3 : 4
+> CHANGE  3 : 5  @  3 : 4
 
-> INSERT  1 : 6  @  1
+~ import java.util.Collection;
+~ import java.util.Collections;
+
+> INSERT  1 : 8  @  1
 
 + 
 + import com.google.common.collect.ImmutableSet;
 + 
 + import net.lax1dude.eaglercraft.v1_8.EagRuntime;
++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack;
++ import net.lax1dude.eaglercraft.v1_8.minecraft.ResourceIndex;
 + import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
 
 > DELETE  1  @  1 : 3
 
-> CHANGE  5 : 6  @  5 : 7
+> CHANGE  4 : 6  @  4 : 7
 
+~ public class DefaultResourcePack extends ResourceIndex implements IResourcePack {
 ~ 	public static final Set<String> defaultResourceDomains = ImmutableSet.of("minecraft", "eagler");
 
-> DELETE  1  @  1 : 5
+> CHANGE  1 : 13  @  1 : 3
 
-> CHANGE  15 : 16  @  15 : 17
+~ 	private final Collection<String> propertyFilesIndex;
+~ 
+~ 	public DefaultResourcePack() {
+~ 		String str = EagRuntime.getResourceString("/assets/minecraft/optifine/_property_files_index.json");
+~ 		if (str != null) {
+~ 			Collection<String> lst = EaglerFolderResourcePack.loadPropertyFileList(str);
+~ 			if (lst != null) {
+~ 				propertyFilesIndex = lst;
+~ 				return;
+~ 			}
+~ 		}
+~ 		propertyFilesIndex = Collections.emptyList();
+
+> CHANGE  17 : 18  @  17 : 19
 
 ~ 		return null;
 
@@ -52,4 +71,22 @@
 ~ 	public ImageData getPackImage() throws IOException {
 ~ 		return TextureUtil.readBufferedImage(EagRuntime.getRequiredResourceStream("pack.png"));
 
+> INSERT  5 : 20  @  5
+
++ 
++ 	@Override
++ 	public ResourceIndex getEaglerFileIndex() {
++ 		return this;
++ 	}
++ 
++ 	@Override
++ 	protected Collection<String> getPropertiesFiles0() {
++ 		return propertyFilesIndex;
++ 	}
++ 
++ 	@Override
++ 	protected Collection<String> getCITPotionsFiles0() {
++ 		return Collections.emptyList();
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java
index 03f76d57..abb9e4e9 100644
--- a/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java
+++ b/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java
@@ -7,13 +7,23 @@
 
 > DELETE  2  @  2 : 3
 
-> INSERT  3 : 5  @  3
+> INSERT  2 : 3  @  2
+
++ import java.util.Collection;
+
+> INSERT  1 : 4  @  1
 
 + 
++ import net.lax1dude.eaglercraft.v1_8.minecraft.ResourceIndex;
 + import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
 
 > CHANGE  13 : 14  @  13 : 14
 
 ~ 	ImageData getPackImage() throws IOException;
 
+> INSERT  2 : 4  @  2
+
++ 
++ 	ResourceIndex getEaglerFileIndex();
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java
index c2523cc9..08b8a0d7 100644
--- a/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java
+++ b/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java
@@ -13,7 +13,7 @@
 
 > DELETE  7  @  7 : 8
 
-> INSERT  1 : 16  @  1
+> INSERT  1 : 18  @  1
 
 + import java.util.Set;
 + 
@@ -29,13 +29,22 @@
 + import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
 + import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.BlockVertexIDs;
 + import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.VertexMarkerState;
++ import net.minecraft.block.Block;
++ import net.minecraft.block.state.IBlockState;
 + import net.minecraft.client.Minecraft;
 
-> DELETE  9  @  9 : 10
+> INSERT  8 : 10  @  8
+
++ import net.minecraft.client.renderer.block.statemap.IStateMapper;
++ import net.minecraft.client.renderer.block.statemap.StateMapperBase;
+
+> DELETE  1  @  1 : 2
 
 > DELETE  3  @  3 : 9
 
-> DELETE  7  @  7 : 10
+> CHANGE  7 : 8  @  7 : 10
+
+~ import net.optifine.CustomItems;
 
 > CHANGE  20 : 21  @  20 : 21
 
@@ -85,12 +94,44 @@
 
 ~ 			return modelblock;
 
-> CHANGE  21 : 23  @  21 : 22
+> INSERT  4 : 5  @  4
+
++ 		String path = parResourceLocation.getResourcePath();
+
+> CHANGE  1 : 2  @  1 : 2
+
+~ 				((path.startsWith("mcpatcher/") || path.startsWith("optifine/")) ? "" : "models/") + path + ".json");
+
+> CHANGE  15 : 17  @  15 : 16
 
 ~ 								+ Item.itemRegistry.getNameForObject(item) + "\'");
 ~ 						LOGGER.warn(exception);
 
-> INSERT  132 : 133  @  132
+> INSERT  7 : 23  @  7
+
++ 	public void loadItemModel(String p_loadItemModel_1_, ResourceLocation p_loadItemModel_2_,
++ 			ResourceLocation p_loadItemModel_3_) {
++ 		this.itemLocations.put(p_loadItemModel_1_, p_loadItemModel_2_);
++ 
++ 		if (this.models.get(p_loadItemModel_2_) == null) {
++ 			try {
++ 				ModelBlock modelblock = this.loadModel(p_loadItemModel_2_);
++ 				this.models.put(p_loadItemModel_2_, modelblock);
++ 			} catch (Exception exception) {
++ 				LOGGER.warn("Unable to load item model: \'{}\' for item: \'{}\'",
++ 						new Object[] { p_loadItemModel_2_, p_loadItemModel_3_ });
++ 				LOGGER.warn(exception.getClass().getName() + ": " + exception.getMessage());
++ 			}
++ 		}
++ 	}
++ 
+
+> INSERT  107 : 109  @  107
+
++ 		CustomItems.update();
++ 		CustomItems.loadModels(this);
+
+> INSERT  18 : 19  @  18
 
 + 		boolean deferred = Minecraft.getMinecraft().gameSettings.shaders;
 
@@ -168,4 +209,38 @@
 
 ~ 		for (EaglerTextureAtlasSprite textureatlassprite : this.sprites.values()) {
 
+> INSERT  11 : 42  @  11
+
++ 	// eagler hack
++ 	public String getBaseTextureForBlockPre(int blockId, int metadata) {
++ 		Block block = Block.blockRegistry.getObjectById(blockId);
++ 		if (block != null) {
++ 			IBlockState state = block.getStateFromMeta(metadata);
++ 			if (state != null) {
++ 				IStateMapper mapper = blockModelShapes.getBlockStateMapper().blockStateMap.get(block);
++ 				ModelResourceLocation loc = null;
++ 				if (mapper != null) {
++ 					loc = mapper.putStateModelLocations(block).get(state);
++ 				}
++ 				if (loc == null) {
++ 					loc = new ModelResourceLocation(Block.blockRegistry.getNameForObject(block),
++ 							StateMapperBase.getPropertyString(state.getProperties()));
++ 				}
++ 				ModelBlockDefinition.Variants v = variants.get(loc);
++ 				if (v != null && v.getVariants().size() > 0) {
++ 					ModelBlockDefinition.Variant vv = v.getVariants().get(0);
++ 					ModelBlock model = this.models.get(vv.getModelLocation());
++ 					if (model != null) {
++ 						String name = model.resolveTextureName("particle");
++ 						if (name != null) {
++ 							return name;
++ 						}
++ 					}
++ 				}
++ 			}
++ 		}
++ 		return null;
++ 	}
++ 
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java
index 3df906f3..cea7ec8e 100644
--- a/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java
+++ b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java
@@ -7,4 +7,20 @@
 
 > DELETE  6  @  6 : 9
 
+> INSERT  8 : 10  @  8
+
++ 	public ModelBakery modelbakerytmp; // eagler hack
++ 
+
+> CHANGE  6 : 14  @  6 : 10
+
+~ 		modelbakerytmp = new ModelBakery(iresourcemanager, this.texMap, this.modelProvider);
+~ 		try {
+~ 			this.modelRegistry = modelbakerytmp.setupModelRegistry();
+~ 			this.defaultModel = (IBakedModel) this.modelRegistry.getObject(ModelBakery.MODEL_MISSING);
+~ 			this.modelProvider.reloadModels();
+~ 		} finally {
+~ 			modelbakerytmp = null;
+~ 		}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java b/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java
index e520bb4d..ff3e01ab 100644
--- a/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java
+++ b/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java
@@ -52,7 +52,9 @@
 
 + import net.minecraft.util.EnumChatFormatting;
 
-> DELETE  2  @  2 : 8
+> CHANGE  2 : 3  @  2 : 8
+
+~ import net.optifine.CustomSky;
 
 > DELETE  3  @  3 : 8
 
@@ -124,7 +126,7 @@
 + 	public boolean hideDefaultUsernameWarning = false;
 + 	public boolean hideVideoSettingsWarning = EagRuntime.getPlatformType() == EnumPlatformType.DESKTOP;
 
-> CHANGE  1 : 15  @  1 : 2
+> CHANGE  1 : 21  @  1 : 2
 
 ~ 	public int voiceListenRadius = 16;
 ~ 	public float voiceListenVolume = 0.5f;
@@ -139,6 +141,12 @@
 ~ 	public float screenRecordGameVolume = ScreenRecordingController.DEFAULT_GAME_VOLUME;
 ~ 	public float screenRecordMicVolume = ScreenRecordingController.DEFAULT_MIC_VOLUME;
 ~ 
+~ 	public int betterGrassOF = 0;
+~ 	public int connectedTexturesOF = 1;
+~ 	public boolean customSkyOF = true;
+~ 	public boolean smartLeavesOF = false;
+~ 	public boolean customItemsOF = true;
+~ 
 ~ 	public GameSettings(Minecraft mcIn) {
 
 > CHANGE  4 : 6  @  4 : 7
@@ -240,53 +248,77 @@
 ~ 		if (parOptions == GameSettings.Options.HUD_STATS) {
 ~ 			this.hudStats = !this.hudStats;
 
-> CHANGE  2 : 4  @  2 : 5
+> CHANGE  2 : 32  @  2 : 3
 
 ~ 		if (parOptions == GameSettings.Options.HUD_WORLD) {
 ~ 			this.hudWorld = !this.hudWorld;
-
-> CHANGE  2 : 4  @  2 : 5
-
+~ 		}
+~ 
 ~ 		if (parOptions == GameSettings.Options.HUD_24H) {
 ~ 			this.hud24h = !this.hud24h;
-
-> CHANGE  2 : 4  @  2 : 5
-
+~ 		}
+~ 
 ~ 		if (parOptions == GameSettings.Options.CHUNK_FIX) {
 ~ 			this.chunkFix = !this.chunkFix;
-
-> CHANGE  2 : 4  @  2 : 4
-
+~ 		}
+~ 
 ~ 		if (parOptions == GameSettings.Options.FOG) {
 ~ 			this.fog = !this.fog;
+~ 		}
+~ 
+~ 		if (parOptions == GameSettings.Options.FXAA) {
+~ 			this.fxaa = (this.fxaa + parInt1) % 3;
+~ 		}
+~ 
+~ 		if (parOptions == GameSettings.Options.FULLSCREEN) {
+~ 			this.mc.toggleFullscreen();
+~ 		}
+~ 
+~ 		if (parOptions == GameSettings.Options.FNAW_SKINS) {
+~ 			this.enableFNAWSkins = !this.enableFNAWSkins;
+~ 			this.mc.getRenderManager().setEnableFNAWSkins(this.mc.getEnableFNAWSkins());
+~ 		}
+~ 
+~ 		if (parOptions == GameSettings.Options.EAGLER_VSYNC) {
+
+> DELETE  1  @  1 : 2
 
 > CHANGE  2 : 4  @  2 : 4
 
-~ 		if (parOptions == GameSettings.Options.FXAA) {
-~ 			this.fxaa = (this.fxaa + parInt1) % 3;
+~ 		if (parOptions == GameSettings.Options.EAGLER_DYNAMIC_LIGHTS) {
+~ 			this.enableDynamicLights = !this.enableDynamicLights;
 
-> INSERT  2 : 24  @  2
+> CHANGE  3 : 9  @  3 : 5
 
-+ 		if (parOptions == GameSettings.Options.FULLSCREEN) {
-+ 			this.mc.toggleFullscreen();
-+ 		}
-+ 
-+ 		if (parOptions == GameSettings.Options.FNAW_SKINS) {
-+ 			this.enableFNAWSkins = !this.enableFNAWSkins;
-+ 			this.mc.getRenderManager().setEnableFNAWSkins(this.mc.getEnableFNAWSkins());
-+ 		}
-+ 
-+ 		if (parOptions == GameSettings.Options.EAGLER_VSYNC) {
-+ 			this.enableVsync = !this.enableVsync;
-+ 		}
-+ 
-+ 		if (parOptions == GameSettings.Options.EAGLER_DYNAMIC_LIGHTS) {
-+ 			this.enableDynamicLights = !this.enableDynamicLights;
+~ 		if (parOptions == GameSettings.Options.EAGLER_PROFANITY_FILTER) {
+~ 			this.enableProfanityFilter = !this.enableProfanityFilter;
+~ 		}
+~ 
+~ 		if (parOptions == GameSettings.Options.OF_CONNECTED_TEXTURES) {
+~ 			this.connectedTexturesOF = (this.connectedTexturesOF + 1) % 3;
+
+> CHANGE  3 : 6  @  3 : 5
+
+~ 		if (parOptions == GameSettings.Options.OF_BETTER_GRASS) {
+~ 			this.betterGrassOF = (this.betterGrassOF + 1) % 3;
+~ 			this.mc.renderGlobal.loadRenderers();
+
+> CHANGE  2 : 5  @  2 : 4
+
+~ 		if (parOptions == GameSettings.Options.OF_CUSTOM_SKIES) {
+~ 			this.customSkyOF = !this.customSkyOF;
+~ 			CustomSky.update();
+
+> INSERT  2 : 12  @  2
+
++ 		if (parOptions == GameSettings.Options.OF_SMART_LEAVES) {
++ 			this.smartLeavesOF = !this.smartLeavesOF;
 + 			this.mc.renderGlobal.loadRenderers();
 + 		}
 + 
-+ 		if (parOptions == GameSettings.Options.EAGLER_PROFANITY_FILTER) {
-+ 			this.enableProfanityFilter = !this.enableProfanityFilter;
++ 		if (parOptions == GameSettings.Options.OF_CUSTOM_ITEMS) {
++ 			this.customItemsOF = !this.customItemsOF;
++ 			this.mc.renderGlobal.loadRenderers();
 + 		}
 + 
 
@@ -298,7 +330,7 @@
 
 > DELETE  20  @  20 : 30
 
-> INSERT  8 : 34  @  8
+> INSERT  8 : 40  @  8
 
 + 		case HUD_COORDS:
 + 			return this.hudCoords;
@@ -326,6 +358,12 @@
 + 			return this.enableDynamicLights;
 + 		case EAGLER_PROFANITY_FILTER:
 + 			return this.enableProfanityFilter;
++ 		case OF_CUSTOM_SKIES:
++ 			return this.customSkyOF;
++ 		case OF_SMART_LEAVES:
++ 			return this.smartLeavesOF;
++ 		case OF_CUSTOM_ITEMS:
++ 			return this.customItemsOF;
 
 > CHANGE  43 : 46  @  43 : 47
 
@@ -374,7 +412,7 @@
 
 > DELETE  11  @  11 : 19
 
-> INSERT  9 : 17  @  9
+> INSERT  9 : 33  @  9
 
 + 		} else if (parOptions == GameSettings.Options.FXAA) {
 + 			if (this.fxaa == 0) {
@@ -384,6 +422,22 @@
 + 			} else {
 + 				return s + I18n.format("options.off");
 + 			}
++ 		} else if (parOptions == GameSettings.Options.OF_CONNECTED_TEXTURES) {
++ 			if (this.connectedTexturesOF == 0) {
++ 				return s + I18n.format("options.off");
++ 			} else if (this.connectedTexturesOF == 1) {
++ 				return s + I18n.format("options.graphics.fast");
++ 			} else {
++ 				return s + I18n.format("options.graphics.fancy");
++ 			}
++ 		} else if (parOptions == GameSettings.Options.OF_BETTER_GRASS) {
++ 			if (this.betterGrassOF == 0) {
++ 				return s + I18n.format("options.off");
++ 			} else if (this.betterGrassOF == 1) {
++ 				return s + I18n.format("options.graphics.fast");
++ 			} else {
++ 				return s + I18n.format("options.graphics.fancy");
++ 			}
 
 > CHANGE  6 : 12  @  6 : 10
 
@@ -547,7 +601,7 @@
 
 ~ 					for (EnumPlayerModelParts enumplayermodelparts : EnumPlayerModelParts._VALUES) {
 
-> INSERT  4 : 74  @  4
+> INSERT  4 : 94  @  4
 
 + 
 + 					if (astring[0].equals("enableFNAWSkins")) {
@@ -618,6 +672,26 @@
 + 						hideVideoSettingsWarning = astring[1].equals("true");
 + 					}
 + 
++ 					if (astring[0].equals("betterGrassOF")) {
++ 						betterGrassOF = Integer.parseInt(astring[1]);
++ 					}
++ 
++ 					if (astring[0].equals("connectedTexturesOF")) {
++ 						connectedTexturesOF = Integer.parseInt(astring[1]);
++ 					}
++ 
++ 					if (astring[0].equals("customSkyOF")) {
++ 						customSkyOF = astring[1].equals("true");
++ 					}
++ 
++ 					if (astring[0].equals("smartLeavesOF")) {
++ 						smartLeavesOF = astring[1].equals("true");
++ 					}
++ 
++ 					if (astring[0].equals("customItemsOF")) {
++ 						customItemsOF = astring[1].equals("true");
++ 					}
++ 
 + 					deferredShaderConf.readOption(astring[0], astring[1]);
 
 > CHANGE  6 : 23  @  6 : 7
@@ -681,7 +755,7 @@
 
 > DELETE  9  @  9 : 20
 
-> INSERT  5 : 37  @  5
+> INSERT  5 : 42  @  5
 
 + 			printwriter.println("hudFps:" + this.hudFps);
 + 			printwriter.println("hudWorld:" + this.hudWorld);
@@ -715,6 +789,11 @@
 + 			printwriter.println("touchControlOpacity:" + this.touchControlOpacity);
 + 			printwriter.println("hideDefaultUsernameWarning:" + this.hideDefaultUsernameWarning);
 + 			printwriter.println("hideVideoSettingsWarning:" + this.hideVideoSettingsWarning);
++ 			printwriter.println("betterGrassOF:" + this.betterGrassOF);
++ 			printwriter.println("connectedTexturesOF:" + this.connectedTexturesOF);
++ 			printwriter.println("customSkyOF:" + this.customSkyOF);
++ 			printwriter.println("smartLeavesOF:" + this.smartLeavesOF);
++ 			printwriter.println("customItemsOF:" + this.customItemsOF);
 
 > CHANGE  5 : 8  @  5 : 6
 
@@ -780,7 +859,7 @@
 
 > DELETE  8  @  8 : 10
 
-> CHANGE  16 : 26  @  16 : 17
+> CHANGE  16 : 29  @  16 : 17
 
 ~ 		ENTITY_SHADOWS("options.entityShadows", false, true), HUD_FPS("options.hud.fps", false, true),
 ~ 		HUD_COORDS("options.hud.coords", false, true), HUD_STATS("options.hud.stats", false, true),
@@ -791,6 +870,9 @@
 ~ 		FNAW_SKINS("options.skinCustomisation.enableFNAWSkins", false, true),
 ~ 		EAGLER_VSYNC("options.vsync", false, true), EAGLER_DYNAMIC_LIGHTS("options.dynamicLights", false, true),
 ~ 		EAGLER_PROFANITY_FILTER("options.profanityFilterButton", false, true),
-~ 		EAGLER_TOUCH_CONTROL_OPACITY("options.touchControlOpacity", true, false);
+~ 		EAGLER_TOUCH_CONTROL_OPACITY("options.touchControlOpacity", true, false),
+~ 		OF_CONNECTED_TEXTURES("options.connectedTexturesOF", false, false),
+~ 		OF_BETTER_GRASS("options.betterGrassOF", false, false), OF_CUSTOM_SKIES("options.customSkiesOF", false, true),
+~ 		OF_SMART_LEAVES("options.smartLeavesOF", false, true), OF_CUSTOM_ITEMS("options.customItemsOF", false, true);
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java b/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java
index 6f8ec08d..b94f3e63 100644
--- a/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java
+++ b/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java
@@ -14,7 +14,11 @@
 + import com.carrotsearch.hppc.cursors.IntObjectCursor;
 + import com.carrotsearch.hppc.cursors.ObjectCursor;
 
-> DELETE  2  @  2 : 5
+> CHANGE  2 : 5  @  2 : 5
+
+~ 
+~ import java.util.Arrays;
+~ import java.util.Collections;
 
 > CHANGE  1 : 3  @  1 : 4
 
@@ -110,7 +114,23 @@
 
 ~ 	public ObjectContainer<PotionEffect> getActivePotionEffects() {
 
-> CHANGE  4 : 5  @  4 : 5
+> INSERT  3 : 16  @  3
+
++ 	public List<PotionEffect> getActivePotionEffectsList() {
++ 		if (activePotionsMap.isEmpty()) {
++ 			return Collections.emptyList();
++ 		}
++ 		PotionEffect[] arr = this.activePotionsMap.values().toArray(PotionEffect.class);
++ 		if (arr.length > 1) {
++ 			Arrays.sort(arr, (p1, p2) -> {
++ 				return p1.getPotionID() - p2.getPotionID();
++ 			});
++ 		}
++ 		return Arrays.asList(arr);
++ 	}
++ 
+
+> CHANGE  1 : 2  @  1 : 2
 
 ~ 		return this.activePotionsMap.containsKey(potionId);
 
diff --git a/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java b/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java
index 1cee1802..a9ee2041 100644
--- a/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java
+++ b/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java
@@ -13,7 +13,11 @@
 + import com.google.common.collect.Lists;
 + 
 
-> CHANGE  59 : 60  @  59 : 60
+> INSERT  19 : 20  @  19
+
++ 		chunkIn.alfheim$getLightingEngine().processLightUpdates();
+
+> CHANGE  40 : 41  @  40 : 41
 
 ~ 		ArrayList<ExtendedBlockStorage> arraylist = Lists.newArrayList();
 
diff --git a/patches/minecraft/net/minecraft/util/BlockPos.edit.java b/patches/minecraft/net/minecraft/util/BlockPos.edit.java
index 0b0ddaf1..4ba98c94 100644
--- a/patches/minecraft/net/minecraft/util/BlockPos.edit.java
+++ b/patches/minecraft/net/minecraft/util/BlockPos.edit.java
@@ -15,7 +15,18 @@
 
 > DELETE  1  @  1 : 5
 
-> INSERT  12 : 16  @  12
+> CHANGE  3 : 11  @  3 : 11
+
+~ 	public static final int NUM_X_BITS = 26;
+~ 	public static final int NUM_Z_BITS = NUM_X_BITS;
+~ 	public static final int NUM_Y_BITS = 64 - NUM_X_BITS - NUM_Z_BITS;
+~ 	public static final int Y_SHIFT = 0 + NUM_Z_BITS;
+~ 	public static final int X_SHIFT = Y_SHIFT + NUM_Y_BITS;
+~ 	public static final long X_MASK = (1L << NUM_X_BITS) - 1L;
+~ 	public static final long Y_MASK = (1L << NUM_Y_BITS) - 1L;
+~ 	public static final long Z_MASK = (1L << NUM_Z_BITS) - 1L;
+
+> INSERT  1 : 5  @  1
 
 + 	public BlockPos() {
 + 		super(0, 0, 0);
diff --git a/patches/minecraft/net/minecraft/util/EnumFacing.edit.java b/patches/minecraft/net/minecraft/util/EnumFacing.edit.java
index 3e4fc29c..41f6c677 100644
--- a/patches/minecraft/net/minecraft/util/EnumFacing.edit.java
+++ b/patches/minecraft/net/minecraft/util/EnumFacing.edit.java
@@ -19,7 +19,11 @@
 + 	public static final EnumFacing[] _VALUES = values();
 + 
 
-> CHANGE  162 : 164  @  162 : 164
+> CHANGE  8 : 9  @  8 : 9
+
+~ 	public static final EnumFacing[] HORIZONTALS = new EnumFacing[4];
+
+> CHANGE  153 : 155  @  153 : 155
 
 ~ 	public static EnumFacing random(EaglercraftRandom rand) {
 ~ 		return _VALUES[rand.nextInt(_VALUES.length)];
diff --git a/patches/minecraft/net/minecraft/world/ChunkCache.edit.java b/patches/minecraft/net/minecraft/world/ChunkCache.edit.java
index 4095d5dc..5fb1fa0d 100644
--- a/patches/minecraft/net/minecraft/world/ChunkCache.edit.java
+++ b/patches/minecraft/net/minecraft/world/ChunkCache.edit.java
@@ -5,24 +5,22 @@
 # Version: 1.0
 # Author: lax1dude
 
-> DELETE  8  @  8 : 12
+> INSERT  2 : 3  @  2
 
-> INSERT  75 : 79  @  75
++ import net.minecraft.block.Block;
 
-+ 	public int getBiomeColorForCoords(BlockPos var1, int index) {
-+ 		return this.worldObj.getBiomeColorForCoords(var1, index);
-+ 	}
-+ 
+> DELETE  6  @  6 : 10
 
-> CHANGE  2 : 3  @  2 : 3
+> CHANGE  47 : 51  @  47 : 49
 
-~ 			return Chunk.getNoSkyLightValue();
+~ 		IBlockState state = getBlockState(blockpos);
+~ 		Block b = state.getBlock();
+~ 		int j = b.alfheim$getLightFor(state, this, EnumSkyBlock.SKY, blockpos);
+~ 		int k = b.alfheim$getLightFor(state, this, EnumSkyBlock.BLOCK, blockpos);
 
-> CHANGE  4 : 8  @  4 : 6
+> CHANGE  26 : 28  @  26 : 53
 
-~ 				EnumFacing[] facings = EnumFacing._VALUES;
-~ 				BlockPos tmp = new BlockPos(0, 0, 0);
-~ 				for (int i = 0; i < facings.length; ++i) {
-~ 					int k = this.getLightFor(pos, parBlockPos.offsetEvenFaster(facings[i], tmp));
+~ 	public int getBiomeColorForCoords(BlockPos var1, int index) {
+~ 		return this.worldObj.getBiomeColorForCoords(var1, index);
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/EnumSkyBlock.edit.java b/patches/minecraft/net/minecraft/world/EnumSkyBlock.edit.java
new file mode 100644
index 00000000..c79c086c
--- /dev/null
+++ b/patches/minecraft/net/minecraft/world/EnumSkyBlock.edit.java
@@ -0,0 +1,13 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> INSERT  7 : 9  @  7
+
++ 	public static final EnumSkyBlock[] _VALUES = values();
++ 
+
+> EOF
diff --git a/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java b/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java
index 0dc2ed20..309a1daa 100644
--- a/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java
+++ b/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java
@@ -12,4 +12,9 @@
 + 	int getBiomeColorForCoords(BlockPos var1, int index);
 + 
 
+> INSERT  5 : 7  @  5
+
++ 
++ 	int getLightFor(final EnumSkyBlock lightType, final BlockPos blockPos);
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/World.edit.java b/patches/minecraft/net/minecraft/world/World.edit.java
index 2fc01b88..811d2e9f 100644
--- a/patches/minecraft/net/minecraft/world/World.edit.java
+++ b/patches/minecraft/net/minecraft/world/World.edit.java
@@ -12,8 +12,10 @@
 + import com.carrotsearch.hppc.LongHashSet;
 + import com.carrotsearch.hppc.LongSet;
 
-> INSERT  3 : 4  @  3
+> INSERT  3 : 6  @  3
 
++ 
++ import dev.redstudio.alfheim.lighting.LightingEngine;
 + 
 
 > DELETE  5  @  5 : 6
@@ -61,21 +63,26 @@
 
 ~ 	protected LongSet activeChunkSet = new LongHashSet();
 
-> INSERT  6 : 7  @  6
+> CHANGE  5 : 6  @  5 : 6
 
-+ 	public final boolean isRemote;
+~ 	public final boolean isRemote;
 
-> CHANGE  1 : 2  @  1 : 3
+> CHANGE  1 : 4  @  1 : 3
 
+~ 	private LightingEngine alfheim$lightingEngine;
+~ 
 ~ 	protected World(ISaveHandler saveHandlerIn, WorldInfo info, WorldProvider providerIn, boolean client) {
 
-> DELETE  5  @  5 : 6
+> DELETE  3  @  3 : 4
+
+> DELETE  1  @  1 : 2
 
 > DELETE  2  @  2 : 3
 
-> INSERT  1 : 2  @  1
+> INSERT  1 : 3  @  1
 
 + 		this.isRemote = client;
++ 		this.alfheim$lightingEngine = new LightingEngine(this);
 
 > CHANGE  17 : 19  @  17 : 18
 
@@ -125,29 +132,27 @@
 
 ~ 							return HString.format("ID #%d (%s // %s)",
 
-> CHANGE  60 : 66  @  60 : 65
+> CHANGE  58 : 60  @  58 : 68
 
-~ 				BlockPos tmp = new BlockPos(0, 0, 0);
-~ 				int i1 = this.getLight(pos.up(tmp), false);
-~ 				int i = this.getLight(pos.east(tmp), false);
-~ 				int j = this.getLight(pos.west(tmp), false);
-~ 				int k = this.getLight(pos.south(tmp), false);
-~ 				int l = this.getLight(pos.north(tmp), false);
+~ 		if (!checkNeighbors)
+~ 			return getLight(pos);
 
-> CHANGE  63 : 64  @  63 : 64
+> CHANGE  1 : 2  @  1 : 4
 
-~ 			return Chunk.getNoSkyLightValue();
+~ 		final IBlockState blockState = getBlockState(pos);
 
-> CHANGE  10 : 16  @  10 : 15
+> CHANGE  1 : 4  @  1 : 23
 
-~ 				BlockPos tmp = new BlockPos();
-~ 				int i1 = this.getLightFor(type, pos.up(tmp));
-~ 				int i = this.getLightFor(type, pos.east(tmp));
-~ 				int j = this.getLightFor(type, pos.west(tmp));
-~ 				int k = this.getLightFor(type, pos.south(tmp));
-~ 				int l = this.getLightFor(type, pos.north(tmp));
+~ 		return Math.max(blockState.getBlock().alfheim$getLightFor(blockState, this, EnumSkyBlock.BLOCK, pos),
+~ 				blockState.getBlock().alfheim$getLightFor(blockState, this, EnumSkyBlock.SKY, pos)
+~ 						- skylightSubtracted);
 
-> CHANGE  59 : 65  @  59 : 60
+> CHANGE  32 : 34  @  32 : 71
+
+~ 		IBlockState state = getBlockState(pos);
+~ 		return state.getBlock().alfheim$getLightFor(state, this, type, pos);
+
+> CHANGE  37 : 43  @  37 : 38
 
 ~ 		if (lightValue < 0) {
 ~ 			j += -lightValue;
@@ -260,11 +265,16 @@
 
 > DELETE  23  @  23 : 24
 
-> CHANGE  5 : 8  @  5 : 6
+> INSERT  1 : 7  @  1
 
-~ 			int l = this.getRenderDistanceChunks() - 1;
-~ 			if (l < 1)
-~ 				l = 1;
++ 		int l = this.getRenderDistanceChunks() - 1;
++ 		if (l > 7)
++ 			l = 7;
++ 		else if (l < 1)
++ 			l = 1;
++ 
+
+> DELETE  4  @  4 : 5
 
 > CHANGE  3 : 4  @  3 : 4
 
@@ -296,51 +306,12 @@
 ~ 				for (int m = 0; m < facings.length; ++m) {
 ~ 					EnumFacing enumfacing = facings[m];
 
-> DELETE  22  @  22 : 23
+> CHANGE  17 : 19  @  17 : 127
 
-> CHANGE  25 : 26  @  25 : 26
+~ 		alfheim$lightingEngine.scheduleLightUpdate(lightType, pos);
+~ 		return true;
 
-~ 								BlockPos blockpos$mutableblockpos = new BlockPos();
-
-> CHANGE  1 : 4  @  1 : 2
-
-~ 								EnumFacing[] facings = EnumFacing._VALUES;
-~ 								for (int m = 0; m < facings.length; ++m) {
-~ 									EnumFacing enumfacing = facings[m];
-
-> DELETE  20  @  20 : 23
-
-> INSERT  6 : 7  @  6
-
-+ 				BlockPos tmp = new BlockPos(0, 0, 0);
-
-> CHANGE  10 : 11  @  10 : 11
-
-~ 							if (this.getLightFor(lightType, blockpos1.west(tmp)) < j6) {
-
-> CHANGE  4 : 5  @  4 : 5
-
-~ 							if (this.getLightFor(lightType, blockpos1.east(tmp)) < j6) {
-
-> CHANGE  4 : 5  @  4 : 5
-
-~ 							if (this.getLightFor(lightType, blockpos1.down(tmp)) < j6) {
-
-> CHANGE  4 : 5  @  4 : 5
-
-~ 							if (this.getLightFor(lightType, blockpos1.up(tmp)) < j6) {
-
-> CHANGE  4 : 5  @  4 : 5
-
-~ 							if (this.getLightFor(lightType, blockpos1.north(tmp)) < j6) {
-
-> CHANGE  4 : 5  @  4 : 5
-
-~ 							if (this.getLightFor(lightType, blockpos1.south(tmp)) < j6) {
-
-> DELETE  8  @  8 : 9
-
-> CHANGE  30 : 33  @  30 : 33
+> CHANGE  28 : 31  @  28 : 31
 
 ~ 				Chunk chunk = this.getChunkFromChunkCoordsIfLoaded(i1, j1);
 ~ 				if (chunk != null) {
@@ -432,4 +403,11 @@
 + 				.getBoolean("loadSpawnChunks"))
 + 			return false;
 
+> INSERT  6 : 10  @  6
+
++ 
++ 	public LightingEngine alfheim$getLightingEngine() {
++ 		return alfheim$lightingEngine;
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/WorldServer.edit.java b/patches/minecraft/net/minecraft/world/WorldServer.edit.java
index dc974847..4e1a5568 100644
--- a/patches/minecraft/net/minecraft/world/WorldServer.edit.java
+++ b/patches/minecraft/net/minecraft/world/WorldServer.edit.java
@@ -42,7 +42,11 @@
 
 > DELETE  6  @  6 : 7
 
-> DELETE  11  @  11 : 12
+> INSERT  1 : 2  @  1
+
++ 		alfheim$getLightingEngine().processLightUpdates();
+
+> DELETE  10  @  10 : 11
 
 > DELETE  1  @  1 : 2
 
diff --git a/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java b/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java
index ecbf353c..bb8f2a49 100644
--- a/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java
+++ b/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java
@@ -5,7 +5,13 @@
 # Version: 1.0
 # Author: lax1dude
 
-> DELETE  4  @  4 : 5
+> CHANGE  4 : 9  @  4 : 5
+
+~ 
+~ import dev.redstudio.alfheim.lighting.LightingEngine;
+~ import dev.redstudio.alfheim.utils.EnumBoundaryFacing;
+~ import dev.redstudio.alfheim.utils.WorldChunkSlice;
+~ 
 
 > INSERT  1 : 2  @  1
 
@@ -36,7 +42,14 @@
 ~ 	private List<BlockPos> tileEntityPosQueue;
 ~ 	private final ChunkCoordIntPair coordsCache;
 
-> CHANGE  8 : 9  @  8 : 9
+> INSERT  1 : 5  @  1
+
++ 	private LightingEngine alfheim$lightingEngine;
++ 	private boolean alfheim$isLightInitialized;
++ 	public short[] alfheim$neighborLightChecks;
++ 
+
+> CHANGE  7 : 8  @  7 : 8
 
 ~ 		this.tileEntityPosQueue = new LinkedList();
 
@@ -44,7 +57,12 @@
 
 + 		this.coordsCache = new ChunkCoordIntPair(x, z);
 
-> CHANGE  38 : 39  @  38 : 39
+> INSERT  7 : 9  @  7
+
++ 
++ 		alfheim$lightingEngine = worldIn != null ? worldIn.alfheim$getLightingEngine() : null;
+
+> CHANGE  31 : 32  @  31 : 32
 
 ~ 		return this.getHeightValue(pos.x & 15, pos.z & 15);
 
@@ -58,40 +76,82 @@
 + 			++EaglerMinecraftServer.counterLightUpdate;
 + 		}
 
-> CHANGE  9 : 12  @  9 : 10
+> DELETE  3  @  3 : 8
+
+> CHANGE  1 : 4  @  1 : 11
+
+~ 		if (!worldObj.isAreaLoaded(new BlockPos((xPosition << 4) + 8, 0, (zPosition << 4) + 8), 16)) {
+~ 			return;
+~ 		}
+
+> CHANGE  1 : 4  @  1 : 5
 
 ~ 		if (!this.worldObj.isRemote) {
 ~ 			++EaglerMinecraftServer.counterLightUpdate;
 ~ 		}
 
-> CHANGE  10 : 13  @  10 : 11
+> CHANGE  1 : 2  @  1 : 2
 
-~ 						EnumFacing[] facings = EnumFacing.Plane.HORIZONTAL.facingsArray;
-~ 						for (int m = 0; m < facings.length; ++m) {
-~ 							EnumFacing enumfacing = facings[m];
+~ 		final WorldChunkSlice slice = new WorldChunkSlice(worldObj.getChunkProvider(), xPosition, zPosition);
 
-> CHANGE  6 : 8  @  6 : 7
+> CHANGE  1 : 5  @  1 : 5
 
-~ 						for (int m = 0; m < facings.length; ++m) {
-~ 							EnumFacing enumfacing1 = facings[m];
+~ 		for (int x = 0; x < 16; ++x) {
+~ 			for (int z = 0; z < 16; ++z) {
+~ 				if (!alfheim$recheckGapsForColumn(slice, x, z))
+~ 					continue;
 
-> DELETE  5  @  5 : 6
+> CHANGE  1 : 3  @  1 : 7
 
-> DELETE  8  @  8 : 10
+~ 				if (parFlag)
+~ 					return;
 
-> CHANGE  94 : 97  @  94 : 95
+> DELETE  1  @  1 : 3
 
-~ 				EnumFacing[] facings = EnumFacing.Plane.HORIZONTAL.facingsArray;
-~ 				for (int m = 0; m < facings.length; ++m) {
-~ 					EnumFacing enumfacing = facings[m];
+> CHANGE  2 : 3  @  2 : 3
 
-> INSERT  7 : 10  @  7
+~ 		isGapLightingUpdated = false;
 
-+ 			if (!this.worldObj.isRemote) {
-+ 				++EaglerMinecraftServer.counterLightUpdate;
-+ 			}
+> CHANGE  24 : 26  @  24 : 29
 
-> CHANGE  9 : 10  @  9 : 10
+~ 		int heightMapY = heightMap[z << 4 | x] & 255;
+~ 		int newHeightMapY = Math.max(y, heightMapY);
+
+> CHANGE  1 : 3  @  1 : 4
+
+~ 		while (newHeightMapY > 0 && getBlockLightOpacity(x, newHeightMapY - 1, z) == 0)
+~ 			--newHeightMapY;
+
+> CHANGE  1 : 3  @  1 : 26
+
+~ 		if (newHeightMapY == heightMapY)
+~ 			return;
+
+> CHANGE  1 : 4  @  1 : 2
+
+~ 		if (!this.worldObj.isRemote) {
+~ 			++EaglerMinecraftServer.counterLightUpdate;
+~ 		}
+
+> CHANGE  1 : 2  @  1 : 7
+
+~ 		heightMap[z << 4 | x] = newHeightMapY;
+
+> CHANGE  1 : 3  @  1 : 5
+
+~ 		if (!worldObj.provider.getHasNoSky())
+~ 			alfheim$relightSkylightColumn(x, z, heightMapY, newHeightMapY);
+
+> CHANGE  1 : 2  @  1 : 7
+
+~ 		final int heightMapY1 = heightMap[z << 4 | x];
+
+> CHANGE  1 : 3  @  1 : 23
+
+~ 		if (heightMapY1 < heightMapMinimum) {
+~ 			heightMapMinimum = heightMapY1;
+
+> CHANGE  8 : 9  @  8 : 9
 
 ~ 		return this.getBlock(x, y, z).getLightOpacity();
 
@@ -148,29 +208,40 @@
 ~ 		int j = pos.y;
 ~ 		int k = pos.z & 15;
 
-> CHANGE  86 : 89  @  86 : 89
+> CHANGE  21 : 23  @  21 : 22
 
-~ 		int i = blockpos.x & 15;
-~ 		int j = blockpos.y;
-~ 		int k = blockpos.z & 15;
+~ 				alfheim$initSkylightForSection(extendedblockstorage);
+~ 				// flag = j >= i1;
 
-> CHANGE  1 : 3  @  1 : 2
+> CHANGE  18 : 19  @  18 : 19
 
-~ 		return extendedblockstorage == null
-~ 				? (this.canSeeSky(blockpos) ? enumskyblock.defaultLightValue : getNoSkyLightValue())
+~ 					// int k1 = block1.getLightOpacity();
 
-> CHANGE  1 : 2  @  1 : 2
+> CHANGE  8 : 12  @  8 : 12
 
-~ 						? (this.worldObj.provider.getHasNoSky() ? getNoSkyLightValue()
+~ //					if (j1 != k1 && (j1 < k1 || this.getLightFor(EnumSkyBlock.SKY, pos) > 0
+~ //							|| this.getLightFor(EnumSkyBlock.BLOCK, pos) > 0)) {
+~ //						this.propagateSkylightOcclusion(i, k);
+~ //					}
 
-> CHANGE  6 : 9  @  6 : 9
+> CHANGE  33 : 35  @  33 : 43
+
+~ 		alfheim$lightingEngine.processLightUpdatesForType(enumskyblock);
+~ 		return alfheim$getCachedLightFor(enumskyblock, blockpos);
+
+> CHANGE  3 : 6  @  3 : 6
 
 ~ 		int j = blockpos.x & 15;
 ~ 		int k = blockpos.y;
 ~ 		int l = blockpos.z & 15;
 
-> CHANGE  19 : 22  @  19 : 22
+> CHANGE  4 : 5  @  4 : 5
 
+~ 			alfheim$initSkylightForSection(storageArrays[k >> 4]);
+
+> CHANGE  14 : 18  @  14 : 17
+
+~ 		alfheim$lightingEngine.processLightUpdates();
 ~ 		int j = blockpos.x & 15;
 ~ 		int k = blockpos.y;
 ~ 		int l = blockpos.z & 15;
@@ -214,7 +285,48 @@
 
 + 		blockpos = new BlockPos(blockpos);
 
-> CHANGE  94 : 96  @  94 : 96
+> INSERT  33 : 71  @  33
+
++ 		for (final EnumFacing facing : EnumFacing.HORIZONTALS) {
++ 			final int xOffset = facing.getFrontOffsetX();
++ 			final int zOffset = facing.getFrontOffsetZ();
++ 
++ 			final Chunk nChunk = worldObj.getChunkProvider().getLoadedChunk(xPosition + xOffset, zPosition + zOffset);
++ 
++ 			if (nChunk == null)
++ 				continue;
++ 
++ 			EnumSkyBlock[] lightTypes = EnumSkyBlock._VALUES;
++ 			EnumFacing.AxisDirection[] axisDirections = EnumFacing.AxisDirection._VALUES;
++ 			for (int ii = 0, ll = lightTypes.length; ii < ll; ++ii) {
++ 				final EnumSkyBlock lightType = lightTypes[ii];
++ 				for (int jj = 0, mm = axisDirections.length; jj < mm; ++jj) {
++ 					final EnumFacing.AxisDirection axisDir = axisDirections[jj];
++ 					// Merge flags upon loading of a chunk. This ensures that all flags are always
++ 					// already on the IN boundary below
++ 					alfheim$mergeFlags(lightType, this, nChunk, facing, axisDir);
++ 					alfheim$mergeFlags(lightType, nChunk, this, facing.getOpposite(), axisDir);
++ 
++ 					// Check everything that might have been canceled due to this chunk not being
++ 					// loaded.
++ 					// Also, pass in chunks if already known
++ 					// The boundary to the neighbor chunk (both ways)
++ 					alfheim$scheduleRelightChecksForBoundary(this, nChunk, null, lightType, xOffset, zOffset, axisDir);
++ 					alfheim$scheduleRelightChecksForBoundary(nChunk, this, null, lightType, -xOffset, -zOffset,
++ 							axisDir);
++ 					// The boundary to the diagonal neighbor (since the checks in that chunk were
++ 					// aborted if this chunk wasn't loaded, see
++ 					// alfheim$scheduleRelightChecksForBoundary)
++ 					alfheim$scheduleRelightChecksForBoundary(nChunk, null, this, lightType,
++ 							(zOffset != 0 ? axisDir.getOffset() : 0), (xOffset != 0 ? axisDir.getOffset() : 0),
++ 							facing.getAxisDirection() == EnumFacing.AxisDirection.POSITIVE
++ 									? EnumFacing.AxisDirection.NEGATIVE
++ 									: EnumFacing.AxisDirection.POSITIVE);
++ 				}
++ 			}
++ 		}
+
+> CHANGE  61 : 63  @  61 : 63
 
 ~ 						&& (predicate == null || predicate.apply((T) entity))) {
 ~ 					list.add((T) entity);
@@ -256,14 +368,366 @@
 ~ 					for (int m = 0; m < facings.length; ++m) {
 ~ 						BlockPos blockpos2 = blockpos1.offset(facings[m]);
 
-> CHANGE  29 : 32  @  29 : 30
+> DELETE  14  @  14 : 27
 
-~ 					EnumFacing[] facings = EnumFacing.Plane.HORIZONTAL.facingsArray;
-~ 					for (int i = 0; i < facings.length; ++i) {
-~ 						EnumFacing enumfacing = facings[i];
+> CHANGE  1 : 3  @  1 : 7
 
-> CHANGE  49 : 50  @  49 : 51
+~ 		if (!alfheim$isLightInitialized)
+~ 			alfheim$initChunkLighting(this, worldObj);
 
-~ 		BlockPos blockpos$mutableblockpos = new BlockPos((this.xPosition << 4) + x, 0, (this.zPosition << 4) + z);
+> CHANGE  1 : 10  @  1 : 5
+
+~ 		for (int x = -1; x <= 1; x++) {
+~ 			for (int z = -1; z <= 1; z++) {
+~ 				if (x == 0 && z == 0)
+~ 					continue;
+~ 
+~ 				final Chunk nChunk = worldObj.getChunkProvider().getLoadedChunk(xPosition + x, zPosition + z);
+~ 
+~ 				if (nChunk == null || !nChunk.alfheim$isLightInitialized())
+~ 					return;
+
+> INSERT  3 : 4  @  3
+
++ 		setLightPopulated(true);
+
+> DELETE  10  @  10 : 64
+
+> INSERT  79 : 413  @  79
+
++ 
++ 	private boolean alfheim$recheckGapsForColumn(final WorldChunkSlice slice, final int x, final int z) {
++ 		final int i = x + (z << 4);
++ 
++ 		if (updateSkylightColumns[i]) {
++ 			updateSkylightColumns[i] = false;
++ 
++ 			final int x1 = (this.xPosition << 4) + x;
++ 			final int z1 = (this.zPosition << 4) + z;
++ 
++ 			alfheim$recheckGapsSkylightNeighborHeight(slice, x1, z1, getHeightValue(x, z),
++ 					alfheim$recheckGapsGetLowestHeight(slice, x1, z1));
++ 
++ 			return true;
++ 		}
++ 
++ 		return false;
++ 	}
++ 
++ 	private int alfheim$recheckGapsGetLowestHeight(final WorldChunkSlice slice, final int x, final int z) {
++ 		int max = Integer.MAX_VALUE;
++ 
++ 		Chunk chunk = slice.getChunkFromWorldCoords(x + 1, z);
++ 
++ 		if (chunk != null)
++ 			max = Math.min(max, chunk.getLowestHeight());
++ 
++ 		chunk = slice.getChunkFromWorldCoords(x, z + 1);
++ 
++ 		if (chunk != null)
++ 			max = Math.min(max, chunk.getLowestHeight());
++ 
++ 		chunk = slice.getChunkFromWorldCoords(x - 1, z);
++ 
++ 		if (chunk != null)
++ 			max = Math.min(max, chunk.getLowestHeight());
++ 
++ 		chunk = slice.getChunkFromWorldCoords(x, z - 1);
++ 
++ 		if (chunk != null)
++ 			max = Math.min(max, chunk.getLowestHeight());
++ 
++ 		return max;
++ 	}
++ 
++ 	private void alfheim$recheckGapsSkylightNeighborHeight(final WorldChunkSlice slice, final int x, final int z,
++ 			final int height, final int max) {
++ 		alfheim$checkSkylightNeighborHeight(slice, x, z, max);
++ 		alfheim$checkSkylightNeighborHeight(slice, x + 1, z, height);
++ 		alfheim$checkSkylightNeighborHeight(slice, x, z + 1, height);
++ 		alfheim$checkSkylightNeighborHeight(slice, x - 1, z, height);
++ 		alfheim$checkSkylightNeighborHeight(slice, x, z - 1, height);
++ 	}
++ 
++ 	private void alfheim$checkSkylightNeighborHeight(final WorldChunkSlice slice, final int x, final int z,
++ 			final int maxValue) {
++ 		Chunk c = slice.getChunkFromWorldCoords(x, z);
++ 		if (c == null)
++ 			return;
++ 
++ 		final int y = c.getHeightValue(x & 15, z & 15);
++ 
++ 		if (y > maxValue)
++ 			alfheim$updateSkylightNeighborHeight(slice, x, z, maxValue, y + 1);
++ 		else if (y < maxValue)
++ 			alfheim$updateSkylightNeighborHeight(slice, x, z, y, maxValue + 1);
++ 	}
++ 
++ 	private void alfheim$updateSkylightNeighborHeight(final WorldChunkSlice slice, final int x, final int z,
++ 			final int startY, final int endY) {
++ 		if (endY < startY)
++ 			return;
++ 
++ 		if (!slice.isLoaded(x, z, 16))
++ 			return;
++ 
++ 		for (int y = startY; y < endY; ++y)
++ 			worldObj.checkLightFor(EnumSkyBlock.SKY, new BlockPos(x, y, z));
++ 
++ 		isModified = true;
++ 	}
++ 
++ 	private static void alfheim$mergeFlags(final EnumSkyBlock lightType, final Chunk inChunk, final Chunk outChunk,
++ 			final EnumFacing dir, final EnumFacing.AxisDirection axisDirection) {
++ 		if (outChunk.alfheim$neighborLightChecks == null)
++ 			return;
++ 
++ 		inChunk.alfheim$initNeighborLightChecks();
++ 
++ 		final int inIndex = alfheim$getFlagIndex(lightType, dir, axisDirection, EnumBoundaryFacing.IN);
++ 		final int outIndex = alfheim$getFlagIndex(lightType, dir.getOpposite(), axisDirection, EnumBoundaryFacing.OUT);
++ 
++ 		inChunk.alfheim$neighborLightChecks[inIndex] |= outChunk.alfheim$neighborLightChecks[outIndex];
++ 		// No need to call Chunk.setModified() since checks are not deleted from
++ 		// outChunk
++ 	}
++ 
++ 	private void alfheim$scheduleRelightChecksForBoundary(final Chunk chunk, Chunk nChunk, Chunk sChunk,
++ 			final EnumSkyBlock lightType, final int xOffset, final int zOffset,
++ 			final EnumFacing.AxisDirection axisDirection) {
++ 		if (chunk.alfheim$neighborLightChecks == null)
++ 			return;
++ 
++ 		final int flagIndex = alfheim$getFlagIndex(lightType, xOffset, zOffset, axisDirection, EnumBoundaryFacing.IN); // OUT
++ 																														// checks
++ 																														// from
++ 																														// neighbor
++ 																														// are
++ 																														// already
++ 																														// merged
++ 
++ 		final int flags = chunk.alfheim$neighborLightChecks[flagIndex];
++ 
++ 		if (flags == 0)
++ 			return;
++ 
++ 		if (nChunk == null) {
++ 			nChunk = worldObj.getChunkProvider().getLoadedChunk(chunk.xPosition + xOffset, chunk.zPosition + zOffset);
++ 
++ 			if (nChunk == null)
++ 				return;
++ 		}
++ 
++ 		if (sChunk == null) {
++ 			sChunk = worldObj.getChunkProvider().getLoadedChunk(
++ 					chunk.xPosition + (zOffset != 0 ? axisDirection.getOffset() : 0),
++ 					chunk.zPosition + (xOffset != 0 ? axisDirection.getOffset() : 0));
++ 
++ 			if (sChunk == null)
++ 				return; // Cancel, since the checks in the corner columns require the corner column of
++ 						// sChunk
++ 		}
++ 
++ 		final int reverseIndex = alfheim$getFlagIndex(lightType, -xOffset, -zOffset, axisDirection,
++ 				EnumBoundaryFacing.OUT);
++ 
++ 		chunk.alfheim$neighborLightChecks[flagIndex] = 0;
++ 
++ 		if (alfheim$neighborLightChecks != null)
++ 			nChunk.alfheim$neighborLightChecks[reverseIndex] = 0; // Clear only now that it's clear that the checks
++ 																	// are processed
++ 
++ 		chunk.setChunkModified();
++ 		nChunk.setChunkModified();
++ 
++ 		// Get the area to check
++ 		// Start in the corner...
++ 		int xMin = chunk.xPosition << 4;
++ 		int zMin = chunk.zPosition << 4;
++ 
++ 		// Move to other side of chunk if the direction is positive
++ 		if ((xOffset | zOffset) > 0) {
++ 			xMin += 15 * xOffset;
++ 			zMin += 15 * zOffset;
++ 		}
++ 
++ 		// Shift to other half if necessary (shift perpendicular to dir)
++ 		if (axisDirection == EnumFacing.AxisDirection.POSITIVE) {
++ 			xMin += 8 * (zOffset & 1); // x & 1 is same as abs(x) for x=-1,0,1
++ 			zMin += 8 * (xOffset & 1);
++ 		}
++ 
++ 		// Get maximal values (shift perpendicular to dir)
++ 		final int xMax = xMin + 7 * (zOffset & 1);
++ 		final int zMax = zMin + 7 * (xOffset & 1);
++ 
++ 		for (int y = 0; y < 16; ++y)
++ 			if ((flags & (1 << y)) != 0)
++ 				for (int x = xMin; x <= xMax; ++x)
++ 					for (int z = zMin; z <= zMax; ++z)
++ 						alfheim$scheduleRelightChecksForColumn(lightType, x, z, y << 4, (y << 4) + 15);
++ 	}
++ 
++ 	private void alfheim$initSkylightForSection(final ExtendedBlockStorage extendedBlockStorage) {
++ 		if (worldObj.provider.getHasNoSky())
++ 			return;
++ 
++ 		for (int x = 0; x < 16; ++x) {
++ 			for (int z = 0; z < 16; ++z) {
++ 				if (getHeightValue(x, z) > extendedBlockStorage.getYLocation())
++ 					continue;
++ 
++ 				for (int y = 0; y < 16; ++y)
++ 					extendedBlockStorage.setExtSkylightValue(x, y, z, EnumSkyBlock.SKY.defaultLightValue);
++ 			}
++ 		}
++ 	}
++ 
++ 	private void alfheim$scheduleRelightChecksForColumn(final EnumSkyBlock lightType, final int x, final int z,
++ 			final int yMin, final int yMax) {
++ 		final BlockPos mutableBlockPos = new BlockPos();
++ 
++ 		for (int y = yMin; y <= yMax; ++y)
++ 			worldObj.checkLightFor(lightType, mutableBlockPos.func_181079_c(x, y, z));
++ 	}
++ 
++ 	private static int alfheim$getFlagIndex(final EnumSkyBlock lightType, final int xOffset, final int zOffset,
++ 			final EnumFacing.AxisDirection axisDirection, final EnumBoundaryFacing boundaryFacing) {
++ 		return (lightType == EnumSkyBlock.BLOCK ? 0 : 16) | ((xOffset + 1) << 2) | ((zOffset + 1) << 1)
++ 				| (axisDirection.getOffset() + 1) | boundaryFacing.ordinal();
++ 	}
++ 
++ 	private static int alfheim$getFlagIndex(final EnumSkyBlock lightType, final EnumFacing facing,
++ 			final EnumFacing.AxisDirection axisDirection, final EnumBoundaryFacing boundaryFacing) {
++ 		return alfheim$getFlagIndex(lightType, facing.getFrontOffsetX(), facing.getFrontOffsetZ(), axisDirection,
++ 				boundaryFacing);
++ 	}
++ 
++ 	private static void alfheim$initChunkLighting(final Chunk chunk, final World world) {
++ 		final int xBase = chunk.xPosition << 4;
++ 		final int zBase = chunk.zPosition << 4;
++ 
++ 		final BlockPos mutableBlockPos = new BlockPos(xBase, 0, zBase);
++ 
++ 		if (world.isAreaLoaded(mutableBlockPos.add(-16, 0, -16), mutableBlockPos.add(31, 255, 31), false)) {
++ 			final ExtendedBlockStorage[] extendedBlockStorage = chunk.getBlockStorageArray();
++ 
++ 			for (int i = 0; i < extendedBlockStorage.length; ++i) {
++ 				final ExtendedBlockStorage storage = extendedBlockStorage[i];
++ 
++ 				if (storage == null)
++ 					continue;
++ 
++ 				int yBase = i * 16;
++ 
++ 				for (int y = 0; y < 16; y++) {
++ 					for (int z = 0; z < 16; z++) {
++ 						for (int x = 0; x < 16; x++) {
++ 							if (storage.getBlockByExtId(x, y, z).getLightValue() > 0) {
++ 								mutableBlockPos.func_181079_c(xBase + x, yBase + y, zBase + z);
++ 								world.checkLightFor(EnumSkyBlock.BLOCK, mutableBlockPos);
++ 							}
++ 						}
++ 					}
++ 				}
++ 			}
++ 
++ 			if (!world.provider.getHasNoSky())
++ 				chunk.alfheim$setSkylightUpdatedPublic();
++ 
++ 			chunk.alfheim$setLightInitialized(true);
++ 		}
++ 	}
++ 
++ 	private void alfheim$relightSkylightColumn(final int x, final int z, final int height1, final int height2) {
++ 		final int yMin = Math.min(height1, height2);
++ 		final int yMax = Math.max(height1, height2) - 1;
++ 
++ 		final ExtendedBlockStorage[] sections = getBlockStorageArray();
++ 
++ 		final int xBase = (xPosition << 4) + x;
++ 		final int zBase = (zPosition << 4) + z;
++ 
++ 		alfheim$scheduleRelightChecksForColumn(EnumSkyBlock.SKY, xBase, zBase, yMin, yMax);
++ 
++ 		if (sections[yMin >> 4] == null && yMin > 0) {
++ 			worldObj.checkLightFor(EnumSkyBlock.SKY, new BlockPos(xBase, yMin - 1, zBase));
++ 		}
++ 
++ 		short emptySections = 0;
++ 
++ 		for (int sec = yMax >> 4; sec >= yMin >> 4; --sec) {
++ 			if (sections[sec] == null) {
++ 				emptySections |= (short) (1 << sec);
++ 			}
++ 		}
++ 
++ 		if (emptySections != 0) {
++ 			for (final EnumFacing facing : EnumFacing.HORIZONTALS) {
++ 				final int xOffset = facing.getFrontOffsetX();
++ 				final int zOffset = facing.getFrontOffsetZ();
++ 
++ 				final boolean neighborColumnExists = (((x + xOffset) | (z + zOffset)) & 16) == 0
++ 						// Checks whether the position is at the specified border (the 16 bit is set for
++ 						// both 15+1 and 0-1)
++ 						|| worldObj.getChunkProvider().getLoadedChunk(xPosition + xOffset, zPosition + zOffset) != null;
++ 
++ 				if (neighborColumnExists) {
++ 					for (int sec = yMax >> 4; sec >= yMin >> 4; --sec) {
++ 						if ((emptySections & (1 << sec)) != 0)
++ 							alfheim$scheduleRelightChecksForColumn(EnumSkyBlock.SKY, xBase + xOffset, zBase + zOffset,
++ 									sec << 4, (sec << 4) + 15);
++ 					}
++ 				} else {
++ 					alfheim$initNeighborLightChecks();
++ 
++ 					final EnumFacing.AxisDirection axisDirection = ((facing.getAxis() == EnumFacing.Axis.X ? z : x)
++ 							& 15) < 8 ? EnumFacing.AxisDirection.NEGATIVE : EnumFacing.AxisDirection.POSITIVE;
++ 					alfheim$neighborLightChecks[alfheim$getFlagIndex(EnumSkyBlock.SKY, facing, axisDirection,
++ 							EnumBoundaryFacing.OUT)] |= emptySections;
++ 
++ 					setChunkModified();
++ 				}
++ 			}
++ 		}
++ 	}
++ 
++ 	public LightingEngine alfheim$getLightingEngine() {
++ 		return alfheim$lightingEngine;
++ 	}
++ 
++ 	public boolean alfheim$isLightInitialized() {
++ 		return alfheim$isLightInitialized;
++ 	}
++ 
++ 	public void alfheim$setLightInitialized(final boolean lightInitialized) {
++ 		alfheim$isLightInitialized = lightInitialized;
++ 	}
++ 
++ 	public void alfheim$setSkylightUpdatedPublic() {
++ 		func_177441_y();
++ 	}
++ 
++ 	public void alfheim$initNeighborLightChecks() {
++ 		if (alfheim$neighborLightChecks == null) {
++ 			alfheim$neighborLightChecks = new short[32];
++ 		}
++ 	}
++ 
++ 	public byte alfheim$getCachedLightFor(final EnumSkyBlock lightType, final BlockPos blockPos) {
++ 		final int x = blockPos.x & 15;
++ 		final int y = blockPos.y;
++ 		final int z = blockPos.z & 15;
++ 
++ 		final ExtendedBlockStorage extendedblockstorage = storageArrays[y >> 4];
++ 
++ 		if (extendedblockstorage == null)
++ 			return canSeeSky(blockPos) ? (byte) lightType.defaultLightValue : 0;
++ 		else if (lightType == EnumSkyBlock.SKY)
++ 			return !worldObj.provider.getHasNoSky() ? (byte) extendedblockstorage.getExtSkylightValue(x, y & 15, z) : 0;
++ 		else
++ 			return lightType == EnumSkyBlock.BLOCK ? (byte) extendedblockstorage.getExtBlocklightValue(x, y & 15, z)
++ 					: (byte) lightType.defaultLightValue;
++ 	}
 
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java b/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java
index 9c7a8420..4fa2e5cd 100644
--- a/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java
+++ b/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java
@@ -11,4 +11,9 @@
 
 > DELETE  5  @  5 : 6
 
+> INSERT  29 : 31  @  29
+
++ 
++ 	Chunk getLoadedChunk(int var1, int var2);
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.edit.java b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.edit.java
index 18fcc1c3..30ada553 100644
--- a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.edit.java
+++ b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.edit.java
@@ -11,23 +11,30 @@
 
 > DELETE  3  @  3 : 4
 
-> DELETE  5  @  5 : 7
+> INSERT  2 : 3  @  2
+
++ import net.minecraft.nbt.NBTTagShort;
+
+> DELETE  3  @  3 : 5
 
 > CHANGE  4 : 6  @  4 : 11
 
 ~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
 ~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
 
-> CHANGE  1 : 3  @  1 : 7
+> CHANGE  1 : 4  @  1 : 7
 
 ~ public abstract class AnvilChunkLoader implements IChunkLoader {
 ~ 	private static final Logger logger = LogManager.getLogger("AnvilChunkLoader");
+~ 	private static final String NEIGHBOR_LIGHT_CHECKS_KEY = "NeighborLightChecks";
 
 > DELETE  1  @  1 : 21
 
-> CHANGE  24 : 25  @  24 : 109
+> CHANGE  24 : 27  @  24 : 109
 
 ~ 	protected void writeChunkToNBT(Chunk chunkIn, World worldIn, NBTTagCompound parNBTTagCompound) {
+~ 		alfheim$writeNeighborLightChecksToNBT(chunkIn, parNBTTagCompound);
+~ 		parNBTTagCompound.setBoolean("LightPopulated", chunkIn.alfheim$isLightInitialized());
 
 > CHANGE  80 : 81  @  80 : 81
 
@@ -42,4 +49,57 @@
 
 ~ 	protected Chunk readChunkFromNBT(World worldIn, NBTTagCompound parNBTTagCompound) {
 
+> INSERT  102 : 105  @  102
+
++ 		alfheim$readNeighborLightChecksFromNBT(chunk, parNBTTagCompound);
++ 		chunk.alfheim$setLightInitialized(parNBTTagCompound.getBoolean("LightPopulated"));
++ 
+
+> INSERT  2 : 46  @  2
+
++ 
++ 	private static void alfheim$readNeighborLightChecksFromNBT(final Chunk chunk, final NBTTagCompound compound) {
++ 		if (!compound.hasKey(NEIGHBOR_LIGHT_CHECKS_KEY, 9)) {
++ 			return;
++ 		}
++ 
++ 		final NBTTagList tagList = compound.getTagList(NEIGHBOR_LIGHT_CHECKS_KEY, 2);
++ 
++ 		if (tagList.tagCount() != 32) {
++ 			return;
++ 		}
++ 
++ 		chunk.alfheim$initNeighborLightChecks();
++ 
++ 		final short[] neighborLightChecks = chunk.alfheim$neighborLightChecks;
++ 
++ 		for (int i = 0; i < 32; ++i) {
++ 			neighborLightChecks[i] = ((NBTTagShort) tagList.get(i)).getShort();
++ 		}
++ 	}
++ 
++ 	private static void alfheim$writeNeighborLightChecksToNBT(final Chunk chunk, final NBTTagCompound compound) {
++ 		final short[] neighborLightChecks = chunk.alfheim$neighborLightChecks;
++ 
++ 		if (neighborLightChecks == null) {
++ 			return;
++ 		}
++ 
++ 		boolean empty = true;
++ 
++ 		final NBTTagList list = new NBTTagList();
++ 
++ 		for (final short flags : neighborLightChecks) {
++ 			list.appendTag(new NBTTagShort(flags));
++ 
++ 			if (flags != 0) {
++ 				empty = false;
++ 			}
++ 		}
++ 
++ 		if (!empty) {
++ 			compound.setTag(NEIGHBOR_LIGHT_CHECKS_KEY, list);
++ 		}
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/ExtendedBlockStorage.edit.java b/patches/minecraft/net/minecraft/world/chunk/storage/ExtendedBlockStorage.edit.java
new file mode 100644
index 00000000..51a230fe
--- /dev/null
+++ b/patches/minecraft/net/minecraft/world/chunk/storage/ExtendedBlockStorage.edit.java
@@ -0,0 +1,59 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> INSERT  15 : 17  @  15
+
++ 	private int alfheim$lightRefCount = -1;
++ 
+
+> CHANGE  46 : 59  @  46 : 47
+
+~ 		if (blockRefCount != 0)
+~ 			return false;
+~ 
+~ 		// -1 indicates the lightRefCount needs to be re-calculated
+~ 		if (alfheim$lightRefCount == -1) {
+~ 			if (alfheim$checkLightArrayEqual(skylightArray, (byte) 255)
+~ 					&& alfheim$checkLightArrayEqual(blocklightArray, (byte) 0))
+~ 				alfheim$lightRefCount = 0; // Lighting is trivial, don't send to clients
+~ 			else
+~ 				alfheim$lightRefCount = 1; // Lighting is not trivial, send to clients
+~ 		}
+~ 
+~ 		return alfheim$lightRefCount == 0;
+
+> INSERT  12 : 13  @  12
+
++ 		alfheim$lightRefCount = -1;
+
+> INSERT  8 : 9  @  8
+
++ 		alfheim$lightRefCount = -1;
+
+> INSERT  44 : 45  @  44
+
++ 		alfheim$lightRefCount = -1;
+
+> INSERT  4 : 5  @  4
+
++ 		alfheim$lightRefCount = -1;
+
+> INSERT  1 : 12  @  1
+
++ 
++ 	private boolean alfheim$checkLightArrayEqual(final NibbleArray storage, final byte targetValue) {
++ 		if (storage == null)
++ 			return true;
++ 
++ 		for (final byte currentByte : storage.getData())
++ 			if (currentByte != targetValue)
++ 				return false;
++ 
++ 		return true;
++ 	}
+
+> EOF
diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderDebug.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderDebug.edit.java
new file mode 100644
index 00000000..b3e731c2
--- /dev/null
+++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderDebug.edit.java
@@ -0,0 +1,15 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> INSERT  131 : 135  @  131
+
++ 
++ 	public Chunk getLoadedChunk(int var1, int var2) {
++ 		return provideChunk(var1, var2);
++ 	}
+
+> EOF
diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderEnd.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderEnd.edit.java
index ed422874..c4a42414 100644
--- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderEnd.edit.java
+++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderEnd.edit.java
@@ -19,4 +19,11 @@
 
 ~ 		this.endRNG = new EaglercraftRandom(parLong1, !worldIn.getWorldInfo().isOldEaglercraftRandom());
 
+> INSERT  246 : 250  @  246
+
++ 
++ 	public Chunk getLoadedChunk(int var1, int var2) {
++ 		return provideChunk(var1, var2);
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderFlat.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderFlat.edit.java
index 05157c7c..a87abf86 100644
--- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderFlat.edit.java
+++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderFlat.edit.java
@@ -55,4 +55,11 @@
 ~ 		for (int m = 0, n = this.structureGenerators.size(); m < n; ++m) {
 ~ 			this.structureGenerators.get(m).generate(this, this.worldObj, i, j, (ChunkPrimer) null);
 
+> INSERT  7 : 11  @  7
+
++ 
++ 	public Chunk getLoadedChunk(int var1, int var2) {
++ 		return provideChunk(var1, var2);
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderGenerate.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderGenerate.edit.java
index 3284fdb9..fdffb875 100644
--- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderGenerate.edit.java
+++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderGenerate.edit.java
@@ -37,4 +37,11 @@
 ~ 		this.ravineGenerator = new MapGenRavine(scramble);
 ~ 		this.oceanMonumentGenerator = new StructureOceanMonument(scramble);
 
+> INSERT  421 : 425  @  421
+
++ 
++ 	public Chunk getLoadedChunk(int var1, int var2) {
++ 		return provideChunk(var1, var2);
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderHell.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderHell.edit.java
index add0bf27..1cbb2639 100644
--- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderHell.edit.java
+++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderHell.edit.java
@@ -27,4 +27,11 @@
 ~ 		this.genNetherBridge = new MapGenNetherBridge(scramble);
 ~ 		this.netherCaveGenerator = new MapGenCavesHell(scramble);
 
+> INSERT  351 : 355  @  351
+
++ 
++ 	public Chunk getLoadedChunk(int var1, int var2) {
++ 		return provideChunk(var1, var2);
++ 	}
+
 > EOF
diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.edit.java
index 6b23f6db..626406cf 100644
--- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.edit.java
+++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.edit.java
@@ -127,4 +127,11 @@
 
 ~ 		return this.id2ChunkMap.size();
 
+> INSERT  8 : 12  @  8
+
++ 
++ 	public Chunk getLoadedChunk(int var1, int var2) {
++ 		return this.id2ChunkMap.get(ChunkCoordIntPair.chunkXZ2Int(var1, var2));
++ 	}
+
 > EOF
diff --git a/patches/resources/assets/minecraft/blockstates/black_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/black_stained_glass_pane.edit.json
new file mode 100644
index 00000000..319c020d
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/black_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "black_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "black_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "black_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "black_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "black_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "black_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "black_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "black_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/blue_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/blue_stained_glass_pane.edit.json
new file mode 100644
index 00000000..12853605
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/blue_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "blue_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "blue_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "blue_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "blue_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "blue_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "blue_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "blue_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "blue_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/brown_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/brown_stained_glass_pane.edit.json
new file mode 100644
index 00000000..04a456c2
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/brown_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "brown_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "brown_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "brown_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "brown_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "brown_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "brown_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "brown_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "brown_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/cyan_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/cyan_stained_glass_pane.edit.json
new file mode 100644
index 00000000..06029991
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/cyan_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "cyan_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "cyan_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "cyan_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "cyan_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "cyan_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "cyan_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "cyan_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "cyan_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/gray_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/gray_stained_glass_pane.edit.json
new file mode 100644
index 00000000..46e30290
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/gray_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "gray_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "gray_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "gray_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "gray_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "gray_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "gray_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "gray_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "gray_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/green_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/green_stained_glass_pane.edit.json
new file mode 100644
index 00000000..7f419f31
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/green_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "green_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "green_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "green_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "green_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "green_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "green_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "green_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "green_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/light_blue_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/light_blue_stained_glass_pane.edit.json
new file mode 100644
index 00000000..3bcd5094
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/light_blue_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "light_blue_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "light_blue_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "light_blue_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "light_blue_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "light_blue_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "light_blue_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "light_blue_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "light_blue_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/lime_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/lime_stained_glass_pane.edit.json
new file mode 100644
index 00000000..500b7875
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/lime_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "lime_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "lime_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "lime_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "lime_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "lime_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "lime_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "lime_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "lime_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/magenta_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/magenta_stained_glass_pane.edit.json
new file mode 100644
index 00000000..c95a15a6
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/magenta_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "magenta_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "magenta_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "magenta_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "magenta_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "magenta_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "magenta_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "magenta_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "magenta_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/orange_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/orange_stained_glass_pane.edit.json
new file mode 100644
index 00000000..2cfa4005
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/orange_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "orange_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "orange_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "orange_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "orange_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "orange_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "orange_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "orange_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "orange_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/pink_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/pink_stained_glass_pane.edit.json
new file mode 100644
index 00000000..de964b26
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/pink_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "pink_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "pink_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "pink_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "pink_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "pink_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "pink_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "pink_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "pink_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/purple_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/purple_stained_glass_pane.edit.json
new file mode 100644
index 00000000..d4a7d2a1
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/purple_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "purple_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "purple_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "purple_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "purple_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "purple_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "purple_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "purple_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "purple_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/red_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/red_stained_glass_pane.edit.json
new file mode 100644
index 00000000..199ba426
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/red_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "red_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "red_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "red_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "red_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "red_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "red_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "red_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "red_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/silver_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/silver_stained_glass_pane.edit.json
new file mode 100644
index 00000000..297916a8
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/silver_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "silver_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "silver_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "silver_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "silver_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "silver_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "silver_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "silver_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "silver_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/white_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/white_stained_glass_pane.edit.json
new file mode 100644
index 00000000..f39f15d7
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/white_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "white_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "white_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "white_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "white_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "white_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "white_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "white_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "white_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/blockstates/yellow_stained_glass_pane.edit.json b/patches/resources/assets/minecraft/blockstates/yellow_stained_glass_pane.edit.json
new file mode 100644
index 00000000..c3d80e48
--- /dev/null
+++ b/patches/resources/assets/minecraft/blockstates/yellow_stained_glass_pane.edit.json
@@ -0,0 +1,25 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  5 : 7  @  5 : 7
+
+~         "east=false,north=false,south=true,west=false": { "model": "yellow_pane_n", "y": 180 },
+~         "east=false,north=false,south=false,west=true": { "model": "yellow_pane_n", "y": 270 },
+
+> CHANGE  1 : 4  @  1 : 4
+
+~         "east=true,north=false,south=true,west=false": { "model": "yellow_pane_ne", "y": 90 },
+~         "east=false,north=false,south=true,west=true": { "model": "yellow_pane_ne", "y": 180 },
+~         "east=false,north=true,south=false,west=true": { "model": "yellow_pane_ne", "y": 270 },
+
+> CHANGE  3 : 6  @  3 : 6
+
+~         "east=true,north=false,south=true,west=true": { "model": "yellow_pane_nse", "y": 90 },
+~         "east=false,north=true,south=true,west=true": { "model": "yellow_pane_nse", "y": 180 },
+~         "east=true,north=true,south=false,west=true": { "model": "yellow_pane_nse", "y": 270 },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/lang/en_US.edit.lang b/patches/resources/assets/minecraft/lang/en_US.edit.lang
index a630d9a0..95db2ec7 100644
--- a/patches/resources/assets/minecraft/lang/en_US.edit.lang
+++ b/patches/resources/assets/minecraft/lang/en_US.edit.lang
@@ -1,6 +1,6 @@
 
 # Eagler Context Redacted Diff
-# Copyright (c) 2024 lax1dude. All rights reserved.
+# Copyright (c) 2025 lax1dude. All rights reserved.
 
 # Version: 1.0
 # Author: lax1dude
@@ -257,7 +257,7 @@
 + eaglercraft.command.clientStub=This command is client side!
 + 
 
-> INSERT  163 : 568  @  163
+> INSERT  163 : 574  @  163
 
 + eaglercraft.singleplayer.busy.killTask=Cancel Task
 + eaglercraft.singleplayer.busy.cancelWarning=Are you sure?
@@ -664,6 +664,12 @@
 + eaglercraft.options.vsyncReEnabled.3=VSync enabled causes bad input lag, sorry!
 + eaglercraft.options.vsyncReEnabled.continue=Continue
 + 
++ eaglercraft.options.connectedTexturesOF=Connected Textures
++ eaglercraft.options.betterGrassOF=Better Grass/Snow
++ eaglercraft.options.customSkiesOF=Custom Skies
++ eaglercraft.options.smartLeavesOF=Smart Leaves
++ eaglercraft.options.customItemsOF=Custom Items
++ 
 
 > INSERT  18 : 19  @  18
 
diff --git a/patches/resources/assets/minecraft/models/block/pane_n.edit.json b/patches/resources/assets/minecraft/models/block/pane_n.edit.json
new file mode 100644
index 00000000..f0982e67
--- /dev/null
+++ b/patches/resources/assets/minecraft/models/block/pane_n.edit.json
@@ -0,0 +1,17 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  10 : 16  @  10 : 16
+
+~                 "down":  { "uv": [ 7, 0, 9, 9 ], "texture": "#edge" },
+~                 "up":    { "uv": [ 7, 0, 9, 9 ], "texture": "#edge" },
+~                 "north": { "uv": [ 7, 0, 9, 16 ], "texture": "#edge", "cullface": "north" },
+~                 "south": { "uv": [ 7, 0, 9, 16 ], "texture": "#edge" },
+~                 "west":  { "uv": [ 0, 0, 9, 16 ], "texture": "#pane" },
+~                 "east":  { "uv": [ 7, 0, 16, 16 ], "texture": "#pane" }
+
+> EOF
diff --git a/patches/resources/assets/minecraft/models/block/pane_ne.edit.json b/patches/resources/assets/minecraft/models/block/pane_ne.edit.json
new file mode 100644
index 00000000..bf669d8a
--- /dev/null
+++ b/patches/resources/assets/minecraft/models/block/pane_ne.edit.json
@@ -0,0 +1,16 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  14 : 15  @  14 : 15
+
+~                 "west":  { "uv": [  0, 0,  9, 16 ], "texture": "#pane" },
+
+> CHANGE  9 : 10  @  9 : 10
+
+~                 "north": { "uv": [  0, 0,  7, 16 ], "texture": "#pane" },
+
+> EOF
diff --git a/patches/resources/assets/minecraft/models/block/pane_ns.edit.json b/patches/resources/assets/minecraft/models/block/pane_ns.edit.json
new file mode 100644
index 00000000..9a2f533c
--- /dev/null
+++ b/patches/resources/assets/minecraft/models/block/pane_ns.edit.json
@@ -0,0 +1,17 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  10 : 16  @  10 : 16
+
+~                 "down":  { "uv": [ 7, 0, 9, 16 ], "texture": "#edge", "cull": false },
+~                 "up":    { "uv": [ 7, 0, 9, 16 ], "texture": "#edge", "cull": false },
+~                 "north": { "uv": [ 7, 0, 9, 16 ], "texture": "#edge" },
+~                 "south": { "uv": [ 7, 0, 9, 16 ], "texture": "#edge" },
+~                 "west":  { "uv": [ 0, 0, 16, 16 ], "texture": "#pane", "cull": false },
+~                 "east":  { "uv": [ 0, 0, 16, 16 ], "texture": "#pane", "cull": false }
+
+> EOF
diff --git a/patches/resources/assets/minecraft/models/block/pane_nse.edit.json b/patches/resources/assets/minecraft/models/block/pane_nse.edit.json
new file mode 100644
index 00000000..0c9c19d4
--- /dev/null
+++ b/patches/resources/assets/minecraft/models/block/pane_nse.edit.json
@@ -0,0 +1,16 @@
+
+# Eagler Context Redacted Diff
+# Copyright (c) 2025 lax1dude. All rights reserved.
+
+# Version: 1.0
+# Author: lax1dude
+
+> CHANGE  14 : 15  @  14 : 15
+
+~                 "west":  { "uv": [  0, 0, 16, 16 ], "texture": "#pane" },
+
+> CHANGE  9 : 10  @  9 : 10
+
+~                 "north": { "uv": [  0, 0,  7, 16 ], "texture": "#pane" },
+
+> EOF
diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
index 693cbbe6..76039e8a 100644
--- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
+++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
@@ -58,6 +58,8 @@ class OpenGLObjects {
 	static class TextureGL implements ITextureGL {
 		
 		final int ptr;
+		int width;
+		int height;
 		
 		TextureGL(int ptr) {
 			this.ptr = ptr;
@@ -71,7 +73,23 @@ class OpenGLObjects {
 		public void free() {
 			PlatformOpenGL._wglDeleteTextures(this);
 		}
-		
+
+		@Override
+		public void setCacheSize(int w, int h) {
+			width = w;
+			height = h;
+		}
+
+		@Override
+		public int getWidth() {
+			return width;
+		}
+
+		@Override
+		public int getHeight() {
+			return height;
+		}
+
 	}
 
 	static class ProgramGL implements IProgramGL {
diff --git a/sources/main/java/dev/redstudio/alfheim/lighting/LightingEngine.java b/sources/main/java/dev/redstudio/alfheim/lighting/LightingEngine.java
new file mode 100644
index 00000000..dd0afdbb
--- /dev/null
+++ b/sources/main/java/dev/redstudio/alfheim/lighting/LightingEngine.java
@@ -0,0 +1,496 @@
+package dev.redstudio.alfheim.lighting;
+
+import dev.redstudio.alfheim.utils.DeduplicatedLongQueue;
+import dev.redstudio.redcore.math.ClampUtil;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.Vec3i;
+import net.minecraft.world.EnumSkyBlock;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
+
+/**
+ * Modified by lax1dude not to abuse interfaces
+ * 
+ * @author Luna Lage (Desoroxxx)
+ * @author kappa-maintainer
+ * @author embeddedt
+ * @author Angeline (@jellysquid)
+ * @since 1.0
+ */
+public final class LightingEngine {
+
+	private static final byte MAX_LIGHT_LEVEL = 15;
+
+	private final World world;
+
+	// Layout of longs: [padding(4)] [y(8)] [x(26)] [z(26)]
+	private final DeduplicatedLongQueue[] lightUpdateQueues = new DeduplicatedLongQueue[EnumSkyBlock.values().length];
+
+	// Layout of longs: see above
+	private final DeduplicatedLongQueue[] darkeningQueues = new DeduplicatedLongQueue[MAX_LIGHT_LEVEL + 1];
+	private final DeduplicatedLongQueue[] brighteningQueues = new DeduplicatedLongQueue[MAX_LIGHT_LEVEL + 1];
+
+	// Layout of longs: [newLight(4)] [pos(60)]
+	private final DeduplicatedLongQueue initialBrightenings;
+	// Layout of longs: [padding(4)] [pos(60)]
+	private final DeduplicatedLongQueue initialDarkenings;
+
+	private boolean updating = false;
+
+	// Layout parameters
+	// Length of bit segments
+	private static final int L_X = 26, L_Y = 8, L_Z = 26, L_L = 4;
+
+	// Bit segment shifts/positions
+	private static final int S_Z = 0, S_X = S_Z + L_Z, S_Y = S_X + L_X, S_L = S_Y + L_Y;
+
+	// Bit segment masks
+	private static final long M_X = (1L << L_X) - 1, M_Y = (1L << L_Y) - 1, M_Z = (1L << L_Z) - 1,
+			M_L = (1L << L_L) - 1, M_POS = (M_Y << S_Y) | (M_X << S_X) | (M_Z << S_Z);
+
+	// Bit to check whether y had overflow
+	private static final long Y_CHECK = 1L << (S_Y + L_Y);
+
+	private static final long[] neighborShifts = new long[6];
+
+	static {
+		for (byte i = 0; i < 6; ++i) {
+			final Vec3i offset = EnumFacing._VALUES[i].getDirectionVec();
+			neighborShifts[i] = ((long) offset.getY() << S_Y) | ((long) offset.getX() << S_X)
+					| ((long) offset.getZ() << S_Z);
+		}
+	}
+
+	// Mask to extract chunk identifier
+	private static final long M_CHUNK = ((M_X >> 4) << (4 + S_X)) | ((M_Z >> 4) << (4 + S_Z));
+
+	// Iteration state data
+	// Cache position to avoid allocation of new object each time
+	private final BlockPos currentPos = new BlockPos();
+	private Chunk currentChunk;
+	private long currentChunkIdentifier;
+	private long currentData;
+
+	// Cached data about neighboring blocks (of tempPos)
+	private boolean isNeighborDataValid = false;
+
+	private final NeighborInfo[] neighborInfos = new NeighborInfo[6];
+	private DeduplicatedLongQueue currentQueue;
+
+	public LightingEngine(final World world) {
+		this.world = world;
+
+		initialBrightenings = new DeduplicatedLongQueue(16384);
+		initialDarkenings = new DeduplicatedLongQueue(16384);
+
+		for (int i = 0; i < EnumSkyBlock.values().length; ++i)
+			lightUpdateQueues[i] = new DeduplicatedLongQueue(16384);
+
+		for (int i = 0; i < darkeningQueues.length; ++i)
+			darkeningQueues[i] = new DeduplicatedLongQueue(16384);
+
+		for (int i = 0; i < brighteningQueues.length; ++i)
+			brighteningQueues[i] = new DeduplicatedLongQueue(16384);
+
+		for (int i = 0; i < neighborInfos.length; ++i)
+			neighborInfos[i] = new NeighborInfo();
+	}
+
+	/**
+	 * Schedules a light update for the specified light type and position to be
+	 * processed later by
+	 * {@link LightingEngine#processLightUpdatesForType(EnumSkyBlock)}
+	 */
+	public void scheduleLightUpdate(final EnumSkyBlock lightType, final BlockPos pos) {
+		scheduleLightUpdate(lightType, encodeWorldCoord(pos));
+	}
+
+	/**
+	 * Schedules a light update for the specified light type and position to be
+	 * processed later by {@link LightingEngine#processLightUpdates()}
+	 */
+	private void scheduleLightUpdate(final EnumSkyBlock lightType, final long blockPos) {
+		lightUpdateQueues[lightType.ordinal()].enqueue(blockPos);
+	}
+
+	/**
+	 * Calls {@link LightingEngine#processLightUpdatesForType(EnumSkyBlock)} for
+	 * both light types
+	 */
+	public void processLightUpdates() {
+		processLightUpdatesForType(EnumSkyBlock.SKY);
+		processLightUpdatesForType(EnumSkyBlock.BLOCK);
+	}
+
+	/**
+	 * Processes light updates of the given light type
+	 */
+	public void processLightUpdatesForType(final EnumSkyBlock lightType) {
+		final DeduplicatedLongQueue queue = lightUpdateQueues[lightType.ordinal()];
+
+		// Quickly check if the queue is empty before we acquire a more expensive lock.
+		if (queue.isEmpty())
+			return;
+
+		processLightUpdatesForTypeInner(lightType, queue);
+	}
+
+	private void processLightUpdatesForTypeInner(final EnumSkyBlock lightType, final DeduplicatedLongQueue queue) {
+		// Avoid nested calls
+		if (updating)
+			throw new IllegalStateException("Already processing updates!");
+
+		updating = true;
+
+		currentChunkIdentifier = -1; // Reset chunk cache
+
+		currentQueue = queue;
+
+		if (currentQueue != null)
+			currentQueue.newDeduplicationSet();
+
+		// Process the queued updates and enqueue them for further processing
+		while (nextItem()) {
+			if (currentChunk == null)
+				continue;
+
+			final byte oldLight = getCursorCachedLight(lightType);
+			final byte newLight = calculateNewLightFromCursor(lightType);
+
+			if (oldLight < newLight)
+				initialBrightenings.enqueue(((long) newLight << S_L) | currentData); // Don't enqueue directly for
+																						// brightening to avoid
+																						// duplicate scheduling
+			else if (oldLight > newLight)
+				initialDarkenings.enqueue(currentData); // Don't enqueue directly for darkening to avoid duplicate
+														// scheduling
+		}
+
+		currentQueue = initialBrightenings;
+
+		if (currentQueue != null)
+			currentQueue.newDeduplicationSet();
+
+		while (nextItem()) {
+			final byte newLight = (byte) (currentData >> S_L & M_L);
+
+			if (newLight > getCursorCachedLight(lightType))
+				enqueueBrightening(currentPos, currentData & M_POS, newLight, currentChunk, lightType); // Sets the
+																										// light to
+																										// newLight to
+																										// only schedule
+																										// once. Clear
+																										// leading bits
+																										// of curData
+																										// for later
+		}
+
+		currentQueue = initialDarkenings;
+
+		if (currentQueue != null)
+			currentQueue.newDeduplicationSet();
+
+		while (nextItem()) {
+			final byte oldLight = getCursorCachedLight(lightType);
+
+			if (oldLight != 0)
+				enqueueDarkening(currentPos, currentData, oldLight, currentChunk, lightType); // Sets the light to zero
+																								// to only schedule once
+		}
+
+		// Iterate through enqueued updates (brightening and darkening in parallel) from
+		// brightest to darkest so that we only need to iterate once
+		for (byte currentLight = MAX_LIGHT_LEVEL; currentLight >= 0; --currentLight) {
+			currentQueue = darkeningQueues[currentLight];
+
+			if (currentQueue != null)
+				currentQueue.newDeduplicationSet();
+
+			while (nextItem()) {
+				// Don't darken if we got brighter due to some other change
+				if (getCursorCachedLight(lightType) >= currentLight)
+					continue;
+
+				final IBlockState blockState = currentChunk.getBlockState(currentPos);
+				final byte luminosity = getCursorLuminosity(blockState, lightType);
+				final byte opacity; // If luminosity is high enough, opacity is irrelevant
+
+				if (luminosity >= MAX_LIGHT_LEVEL - 1)
+					opacity = 1;
+				else
+					opacity = getPosOpacity(currentPos, blockState);
+
+				// Only darken neighbors if we indeed became darker
+				if (calculateNewLightFromCursor(luminosity, opacity, lightType) < currentLight) {
+					// Need to calculate new light value from neighbors IGNORING neighbors which are
+					// scheduled for darkening
+					byte newLight = luminosity;
+
+					fetchNeighborDataFromCursor(lightType);
+
+					for (final NeighborInfo neighborInfo : neighborInfos) {
+						final Chunk neighborChunk = neighborInfo.chunk;
+
+						if (neighborChunk == null)
+							continue;
+
+						final byte neighborLight = neighborInfo.light;
+
+						if (neighborLight == 0)
+							continue;
+
+						final BlockPos neighborPos = neighborInfo.mutableBlockPos;
+
+						if (currentLight - getPosOpacity(neighborPos, neighborChunk
+								.getBlockState(neighborPos)) >= neighborLight) /*
+																				 * Schedule neighbor for darkening if we
+																				 * possibly light it
+																				 */ {
+							enqueueDarkening(neighborPos, neighborInfo.key, neighborLight, neighborChunk, lightType);
+						} else /* Only use for new light calculation if not */ {
+							// If we can't darken the neighbor, no one else can (because of processing
+							// order) -> safe to let us be illuminated by it
+							newLight = (byte) Math.max(newLight, neighborLight - opacity);
+						}
+					}
+
+					// Schedule brightening since light level was set to 0
+					enqueueBrighteningFromCursor(newLight, lightType);
+				} else /*
+						 * We didn't become darker, so we need to re-set our initial light value (was
+						 * set to zero) and notify neighbors
+						 */ {
+					enqueueBrighteningFromCursor(currentLight, lightType); // Do not spread to neighbors immediately to
+																			// avoid scheduling multiple times
+				}
+			}
+
+			currentQueue = brighteningQueues[currentLight];
+
+			if (currentQueue != null)
+				currentQueue.newDeduplicationSet();
+
+			while (nextItem()) {
+				final byte oldLight = getCursorCachedLight(lightType);
+
+				// Only process this if nothing else has happened at this position since
+				// scheduling
+				if (oldLight == currentLight) {
+					world.notifyLightSet(currentPos);
+
+					if (currentLight > 1)
+						spreadLightFromCursor(currentLight, lightType);
+				}
+			}
+		}
+
+		updating = false;
+	}
+
+	/**
+	 * Gets data for neighbors of {@link #currentPos} and saves the results into
+	 * neighbor state data members. If a neighbor can't be accessed/doesn't exist,
+	 * the corresponding entry in neighborChunks is null - others are not reset
+	 */
+	private void fetchNeighborDataFromCursor(final EnumSkyBlock lightType) {
+		// Only update if curPos was changed
+		if (isNeighborDataValid)
+			return;
+
+		isNeighborDataValid = true;
+
+		for (int i = 0; i < neighborInfos.length; ++i) {
+			final NeighborInfo neighborInfo = neighborInfos[i];
+			final long neighborLongPos = neighborInfo.key = currentData + neighborShifts[i];
+
+			if ((neighborLongPos & Y_CHECK) != 0) {
+				neighborInfo.chunk = null;
+				continue;
+			}
+
+			final BlockPos neighborPos = decodeWorldCoord(neighborInfo.mutableBlockPos, neighborLongPos);
+
+			final Chunk neighborChunk;
+
+			if ((neighborLongPos & M_CHUNK) == currentChunkIdentifier)
+				neighborChunk = neighborInfo.chunk = currentChunk;
+			else
+				neighborChunk = neighborInfo.chunk = getChunk(neighborPos);
+
+			if (neighborChunk != null) {
+				final ExtendedBlockStorage neighborSection = neighborChunk
+						.getBlockStorageArray()[neighborPos.getY() >> 4];
+
+				neighborInfo.light = getCachedLightFor(neighborChunk, neighborSection, neighborPos, lightType);
+			}
+		}
+	}
+
+	private static byte getCachedLightFor(final Chunk chunk, final ExtendedBlockStorage storage,
+			final BlockPos blockPos, final EnumSkyBlock type) {
+		final int x = blockPos.getX() & 15;
+		final int y = blockPos.getY();
+		final int z = blockPos.getZ() & 15;
+
+		if (storage == null)
+			return type == EnumSkyBlock.SKY && chunk.canSeeSky(blockPos) ? (byte) type.defaultLightValue : 0;
+		else if (type == EnumSkyBlock.SKY)
+			return chunk.getWorld().provider.getHasNoSky() ? 0 : (byte) storage.getExtSkylightValue(x, y & 15, z);
+		else
+			return type == EnumSkyBlock.BLOCK ? (byte) storage.getExtBlocklightValue(x, y & 15, z)
+					: (byte) type.defaultLightValue;
+	}
+
+	private byte calculateNewLightFromCursor(final EnumSkyBlock lightType) {
+		final IBlockState blockState = currentChunk.getBlockState(currentPos);
+
+		final byte luminosity = getCursorLuminosity(blockState, lightType);
+		final byte opacity;
+
+		if (luminosity >= MAX_LIGHT_LEVEL - 1)
+			opacity = 1;
+		else
+			opacity = getPosOpacity(currentPos, blockState);
+
+		return calculateNewLightFromCursor(luminosity, opacity, lightType);
+	}
+
+	private byte calculateNewLightFromCursor(final byte luminosity, final byte opacity, final EnumSkyBlock lightType) {
+		if (luminosity >= MAX_LIGHT_LEVEL - opacity)
+			return luminosity;
+
+		byte newLight = luminosity;
+
+		fetchNeighborDataFromCursor(lightType);
+
+		for (final NeighborInfo neighborInfo : neighborInfos) {
+			if (neighborInfo.chunk == null)
+				continue;
+
+			newLight = (byte) Math.max(neighborInfo.light - opacity, newLight);
+		}
+
+		return newLight;
+	}
+
+	private void spreadLightFromCursor(final byte currentLight, final EnumSkyBlock lightType) {
+		fetchNeighborDataFromCursor(lightType);
+
+		for (final NeighborInfo neighborInfo : neighborInfos) {
+			final Chunk neighborChunk = neighborInfo.chunk;
+
+			if (neighborChunk == null || currentLight < neighborInfo.light)
+				continue;
+
+			final BlockPos neighborBlockPos = neighborInfo.mutableBlockPos;
+
+			final byte newLight = (byte) (currentLight
+					- getPosOpacity(neighborBlockPos, neighborChunk.getBlockState(neighborBlockPos)));
+
+			if (newLight > neighborInfo.light)
+				enqueueBrightening(neighborBlockPos, neighborInfo.key, newLight, neighborChunk, lightType);
+		}
+	}
+
+	private void enqueueBrighteningFromCursor(final byte newLight, final EnumSkyBlock lightType) {
+		enqueueBrightening(currentPos, currentData, newLight, currentChunk, lightType);
+	}
+
+	/**
+	 * Enqueues the blockPos for brightening and sets its light value to newLight
+	 */
+	private void enqueueBrightening(final BlockPos blockPos, final long longPos, final byte newLight, final Chunk chunk,
+			final EnumSkyBlock lightType) {
+		brighteningQueues[newLight].enqueue(longPos);
+
+		chunk.setLightFor(lightType, blockPos, newLight);
+	}
+
+	/**
+	 * Enqueues the blockPos for darkening and sets its light value to 0
+	 */
+	private void enqueueDarkening(final BlockPos blockPos, final long longPos, final byte oldLight, final Chunk chunk,
+			final EnumSkyBlock lightType) {
+		darkeningQueues[oldLight].enqueue(longPos);
+
+		chunk.setLightFor(lightType, blockPos, 0);
+	}
+
+	private static BlockPos decodeWorldCoord(final BlockPos mutableBlockPos, final long longPos) {
+		return mutableBlockPos.func_181079_c((int) (longPos >> S_X & M_X) - (1 << L_X - 1), (int) (longPos >> S_Y & M_Y),
+				(int) (longPos >> S_Z & M_Z) - (1 << L_Z - 1));
+	}
+
+	private static long encodeWorldCoord(final BlockPos pos) {
+		return ((long) pos.getY() << S_Y) | ((long) pos.getX() + (1 << L_X - 1) << S_X)
+				| ((long) pos.getZ() + (1 << L_Z - 1) << S_Z);
+	}
+
+	/**
+	 * Polls a new item from {@link #currentQueue} and fills in state data members
+	 *
+	 * @return If there was an item to poll
+	 */
+	private boolean nextItem() {
+		if (currentQueue.isEmpty()) {
+			currentQueue = null;
+
+			return false;
+		}
+
+		currentData = currentQueue.dequeue();
+		isNeighborDataValid = false;
+
+		decodeWorldCoord(currentPos, currentData);
+
+		final long chunkIdentifier = currentData & M_CHUNK;
+
+		if (currentChunkIdentifier != chunkIdentifier) {
+			currentChunk = getChunk(currentPos);
+			currentChunkIdentifier = chunkIdentifier;
+		}
+
+		return true;
+	}
+
+	private byte getCursorCachedLight(final EnumSkyBlock lightType) {
+		return currentChunk.alfheim$getCachedLightFor(lightType, currentPos);
+	}
+
+	/**
+	 * Calculates the luminosity for {@link #currentPos}, taking into account the
+	 * light type
+	 */
+	private byte getCursorLuminosity(final IBlockState state, final EnumSkyBlock lightType) {
+		if (lightType == EnumSkyBlock.SKY) {
+			if (currentChunk.canSeeSky(currentPos))
+				return (byte) EnumSkyBlock.SKY.defaultLightValue;
+			else
+				return 0;
+		}
+
+		return (byte) ClampUtil.clampMinFirst(state.getBlock().getLightValue(), 0, MAX_LIGHT_LEVEL);
+	}
+
+	private byte getPosOpacity(final BlockPos blockPos, final IBlockState blockState) {
+		return (byte) ClampUtil.clampMinFirst(blockState.getBlock().getLightOpacity(), 1, MAX_LIGHT_LEVEL);
+	}
+
+	private Chunk getChunk(final BlockPos blockPos) {
+		return world.getChunkProvider().getLoadedChunk(blockPos.getX() >> 4, blockPos.getZ() >> 4);
+	}
+
+	private static final class NeighborInfo {
+
+		public final BlockPos mutableBlockPos = new BlockPos();
+
+		public Chunk chunk;
+
+		public byte light;
+
+		public long key;
+	}
+}
diff --git a/sources/main/java/dev/redstudio/alfheim/utils/DeduplicatedLongQueue.java b/sources/main/java/dev/redstudio/alfheim/utils/DeduplicatedLongQueue.java
new file mode 100644
index 00000000..133e7b7e
--- /dev/null
+++ b/sources/main/java/dev/redstudio/alfheim/utils/DeduplicatedLongQueue.java
@@ -0,0 +1,75 @@
+package dev.redstudio.alfheim.utils;
+
+import com.carrotsearch.hppc.LongArrayDeque;
+import com.carrotsearch.hppc.LongHashSet;
+
+/**
+ * A queue implementation for long values that are deduplicated on addition.
+ * <p>
+ * This is achieved by storing the values in a {@link LongOpenHashSet} and a
+ * {@link LongArrayFIFOQueue}.
+ *
+ * @author Luna Lage (Desoroxxx)
+ * @since 1.3
+ */
+public final class DeduplicatedLongQueue {
+
+	// TODO: Fully Implement my own implementation to get rid of the downsides of
+	// reduce etc...
+
+	private final LongArrayDeque queue;
+	private LongHashSet set;
+
+	/**
+	 * Creates a new deduplicated queue with the given capacity.
+	 *
+	 * @param capacity The capacity of the deduplicated queue
+	 */
+	public DeduplicatedLongQueue(final int capacity) {
+		set = new LongHashSet(capacity);
+		queue = new LongArrayDeque(capacity);
+	}
+
+	/**
+	 * Adds a value to the queue.
+	 *
+	 * @param value The value to add to the queue
+	 */
+	public void enqueue(final long value) {
+		if (set.add(value))
+			queue.addLast(value);
+	}
+
+	/**
+	 * Removes and returns the first value in the queue.
+	 *
+	 * @return The first value in the queue
+	 */
+	public long dequeue() {
+		return queue.removeFirst();
+	}
+
+	/**
+	 * Returns whether the queue is empty.
+	 *
+	 * @return {@code true} if the queue is empty, {@code false} otherwise
+	 */
+	public boolean isEmpty() {
+		return queue.isEmpty();
+	}
+
+	/**
+	 * Creates a new deduplication set.
+	 */
+	public void newDeduplicationSet() {
+		int i = queue.size();
+		if(i < 4) {
+			i = 4;
+		}
+		if((set.keys.length * 3 / 2) > i) {
+			set = new LongHashSet(i);
+		}else {
+			set.clear();
+		}
+	}
+}
diff --git a/sources/main/java/dev/redstudio/alfheim/utils/EnumBoundaryFacing.java b/sources/main/java/dev/redstudio/alfheim/utils/EnumBoundaryFacing.java
new file mode 100644
index 00000000..9daeeed2
--- /dev/null
+++ b/sources/main/java/dev/redstudio/alfheim/utils/EnumBoundaryFacing.java
@@ -0,0 +1,5 @@
+package dev.redstudio.alfheim.utils;
+
+public enum EnumBoundaryFacing {
+	IN, OUT
+}
diff --git a/sources/main/java/dev/redstudio/alfheim/utils/WorldChunkSlice.java b/sources/main/java/dev/redstudio/alfheim/utils/WorldChunkSlice.java
new file mode 100644
index 00000000..58c6b30e
--- /dev/null
+++ b/sources/main/java/dev/redstudio/alfheim/utils/WorldChunkSlice.java
@@ -0,0 +1,88 @@
+package dev.redstudio.alfheim.utils;
+
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.IChunkProvider;
+
+/**
+ * Represents a slice of a world containing a collection of chunks.
+ *
+ * @author Luna Lage (Desoroxxx)
+ * @author Angeline (@jellysquid)
+ * @since 1.0
+ */
+public class WorldChunkSlice {
+
+	private static final int DIAMETER = 5;
+	private static final int RADIUS = DIAMETER / 2;
+
+	private final int x, z;
+
+	private final Chunk[] chunks;
+
+	/**
+	 * Initializes a {@link WorldChunkSlice} object using a given chunk provider and
+	 * coordinates.
+	 *
+	 * @param chunkProvider The chunk provider to get chunks from
+	 * @param x             The X-coordinate of the center chunk
+	 * @param z             The Z-coordinate of the center chunk
+	 */
+	public WorldChunkSlice(final IChunkProvider chunkProvider, final int x, final int z) {
+		chunks = new Chunk[DIAMETER * DIAMETER];
+
+		for (int xDiff = -RADIUS; xDiff <= RADIUS; xDiff++)
+			for (int zDiff = -RADIUS; zDiff <= RADIUS; zDiff++)
+				chunks[((xDiff + RADIUS) * DIAMETER) + (zDiff + RADIUS)] = chunkProvider.getLoadedChunk(x + xDiff,
+						z + zDiff);
+
+		this.x = x - RADIUS;
+		this.z = z - RADIUS;
+	}
+
+	/**
+	 * Checks if all chunks within a radius around a coordinate are loaded.
+	 *
+	 * @param x      The X-coordinate to check around
+	 * @param z      The Z-coordinate to check around
+	 * @param radius The radius around the coordinates to check
+	 *
+	 * @return true if all chunks are loaded, false otherwise
+	 */
+	public boolean isLoaded(final int x, final int z, final int radius) {
+		final int xStart = ((x - radius) >> 4) - this.x;
+		final int zStart = ((z - radius) >> 4) - this.z;
+		final int xEnd = ((x + radius) >> 4) - this.x;
+		final int zEnd = ((z + radius) >> 4) - this.z;
+
+		for (int currentX = xStart; currentX <= xEnd; ++currentX)
+			for (int currentZ = zStart; currentZ <= zEnd; ++currentZ)
+				if (getChunk(currentX, currentZ) == null)
+					return false;
+
+		return true;
+	}
+
+	/**
+	 * Retrieves the chunk that includes the provided world coordinates.
+	 *
+	 * @param x The X-coordinate in the world
+	 * @param z The Z-coordinate in the world
+	 *
+	 * @return The Chunk object that includes these coordinates
+	 */
+	public Chunk getChunkFromWorldCoords(final int x, final int z) {
+		return getChunk((x >> 4) - this.x, (z >> 4) - this.z);
+	}
+
+	/**
+	 * Retrieves the chunk located at the given coordinates within this chunk slice.
+	 *
+	 * @param x The X-coordinate within the slice
+	 * @param z The Z-coordinate within the slice
+	 *
+	 * @return The Chunk object at these coordinates
+	 */
+	private Chunk getChunk(final int x, final int z) {
+		return chunks[(x * DIAMETER) + z];
+	}
+}
diff --git a/sources/main/java/dev/redstudio/redcore/math/ClampUtil.java b/sources/main/java/dev/redstudio/redcore/math/ClampUtil.java
new file mode 100644
index 00000000..91480943
--- /dev/null
+++ b/sources/main/java/dev/redstudio/redcore/math/ClampUtil.java
@@ -0,0 +1,201 @@
+package dev.redstudio.redcore.math;
+
+public class ClampUtil {
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the minimum
+	 * value first.
+	 * <p>
+	 * If the input is less than min, it returns min. If the input is greater than
+	 * max, it returns max. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 *
+	 * @return The clamped value
+	 */
+	public static byte clampMinFirst(final byte input, final byte min, final byte max) {
+		return input < min ? min : input > max ? max : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the minimum
+	 * value first.
+	 * <p>
+	 * If the input is less than min, it returns min. If the input is greater than
+	 * max, it returns max. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 *
+	 * @return The clamped value
+	 */
+	public static short clampMinFirst(final short input, final short min, final short max) {
+		return input < min ? min : input > max ? max : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the minimum
+	 * value first.
+	 * <p>
+	 * If the input is less than min, it returns min. If the input is greater than
+	 * max, it returns max. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 *
+	 * @return The clamped value
+	 */
+	public static int clampMinFirst(final int input, final int min, final int max) {
+		return input < min ? min : input > max ? max : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the minimum
+	 * value first.
+	 * <p>
+	 * If the input is less than min, it returns min. If the input is greater than
+	 * max, it returns max. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 *
+	 * @return The clamped value
+	 */
+	public static long clampMinFirst(final long input, final long min, final long max) {
+		return input < min ? min : input > max ? max : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the minimum
+	 * value first.
+	 * <p>
+	 * If the input is less than min, it returns min. If the input is greater than
+	 * max, it returns max. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 *
+	 * @return The clamped value
+	 */
+	public static float clampMinFirst(final float input, final float min, final float max) {
+		return input < min ? min : input > max ? max : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the minimum
+	 * value first.
+	 * <p>
+	 * If the input is less than min, it returns min. If the input is greater than
+	 * max, it returns max. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 *
+	 * @return The clamped value
+	 */
+	public static double clampMinFirst(final double input, final double min, final double max) {
+		return input < min ? min : input > max ? max : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the maximum
+	 * value first.
+	 * <p>
+	 * If the input is greater than max, it returns max. If the input is less than
+	 * min, it returns min. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 * @return The clamped value
+	 */
+	public static byte clampMaxFirst(final byte input, final byte min, final byte max) {
+		return input > max ? max : input < min ? min : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the maximum
+	 * value first.
+	 * <p>
+	 * If the input is greater than max, it returns max. If the input is less than
+	 * min, it returns min. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 * @return The clamped value
+	 */
+	public static short clampMaxFirst(final short input, final short min, final short max) {
+		return input > max ? max : input < min ? min : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the maximum
+	 * value first.
+	 * <p>
+	 * If the input is greater than max, it returns max. If the input is less than
+	 * min, it returns min. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 * @return The clamped value
+	 */
+	public static int clampMaxFirst(final int input, final int min, final int max) {
+		return input > max ? max : input < min ? min : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the maximum
+	 * value first.
+	 * <p>
+	 * If the input is greater than max, it returns max. If the input is less than
+	 * min, it returns min. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 * @return The clamped value
+	 */
+	public static long clampMaxFirst(final long input, final long min, final long max) {
+		return input > max ? max : input < min ? min : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the maximum
+	 * value first.
+	 * <p>
+	 * If the input is greater than max, it returns max. If the input is less than
+	 * min, it returns min. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 * @return The clamped value
+	 */
+	public static float clampMaxFirst(final float input, final float min, final float max) {
+		return input > max ? max : input < min ? min : input;
+	}
+
+	/**
+	 * Clamps a value within a specified range [min, max], checking for the maximum
+	 * value first.
+	 * <p>
+	 * If the input is greater than max, it returns max. If the input is less than
+	 * min, it returns min. Otherwise, it returns the input.
+	 *
+	 * @param input The input value to clamp
+	 * @param min   The minimum value to clamp to
+	 * @param max   The maximum value to clamp to
+	 * @return The clamped value
+	 */
+	public static double clampMaxFirst(final double input, final double min, final double max) {
+		return input > max ? max : input < min ? min : input;
+	}
+}
diff --git a/sources/main/java/jdk_internal/bidi/Bidi.java b/sources/main/java/jdk_internal/bidi/Bidi.java
index 8c698bea..5ea9ee48 100644
--- a/sources/main/java/jdk_internal/bidi/Bidi.java
+++ b/sources/main/java/jdk_internal/bidi/Bidi.java
@@ -35,7 +35,7 @@
 
 package jdk_internal.bidi;
 
-import jdk_internal.bidi.icu.text.BidiBase;
+import jdk_internal.icu.text.BidiBase;
 
 /**
  * This class implements the Unicode Bidirectional Algorithm.
diff --git a/sources/main/java/jdk_internal/bidi/Normalizer.java b/sources/main/java/jdk_internal/bidi/Normalizer.java
index 8029bd38..ce51a813 100644
--- a/sources/main/java/jdk_internal/bidi/Normalizer.java
+++ b/sources/main/java/jdk_internal/bidi/Normalizer.java
@@ -37,7 +37,7 @@
 
 package jdk_internal.bidi;
 
-import jdk_internal.bidi.icu.text.NormalizerBase;
+import jdk_internal.icu.text.NormalizerBase;
 
 /**
  * This class provides the method {@code normalize} which transforms Unicode
diff --git a/sources/main/java/jdk_internal/bidi/SunNormalizer.java b/sources/main/java/jdk_internal/bidi/SunNormalizer.java
index d1f57bca..f550f2db 100644
--- a/sources/main/java/jdk_internal/bidi/SunNormalizer.java
+++ b/sources/main/java/jdk_internal/bidi/SunNormalizer.java
@@ -25,8 +25,8 @@
 
 package jdk_internal.bidi;
 
-import jdk_internal.bidi.icu.lang.UCharacter;
-import jdk_internal.bidi.icu.text.NormalizerBase;
+import jdk_internal.icu.lang.UCharacter;
+import jdk_internal.icu.text.NormalizerBase;
 
 /**
  * This Normalizer is for Unicode 3.2 support for IDNA only. Developers should
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/BMPSet.java b/sources/main/java/jdk_internal/icu/impl/BMPSet.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/BMPSet.java
rename to sources/main/java/jdk_internal/icu/impl/BMPSet.java
index c7a9999d..78c62aa6 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/BMPSet.java
+++ b/sources/main/java/jdk_internal/icu/impl/BMPSet.java
@@ -32,10 +32,10 @@
  ******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
-import jdk_internal.bidi.icu.text.UnicodeSet.SpanCondition;
-import jdk_internal.bidi.icu.util.OutputInt;
+import jdk_internal.icu.text.UnicodeSet.SpanCondition;
+import jdk_internal.icu.util.OutputInt;
 
 /**
  * Helper class for frozen UnicodeSets, implements contains() and span()
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/CharTrie.java b/sources/main/java/jdk_internal/icu/impl/CharTrie.java
similarity index 98%
rename from sources/main/java/jdk_internal/bidi/icu/impl/CharTrie.java
rename to sources/main/java/jdk_internal/icu/impl/CharTrie.java
index aab129b7..7df38d6c 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/CharTrie.java
+++ b/sources/main/java/jdk_internal/icu/impl/CharTrie.java
@@ -30,12 +30,12 @@
  ******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.DataInputStream;
 import java.io.InputStream;
 
-import jdk_internal.bidi.icu.text.UTF16;
+import jdk_internal.icu.text.UTF16;
 
 import java.io.IOException;
 
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/CharacterIteratorWrapper.java b/sources/main/java/jdk_internal/icu/impl/CharacterIteratorWrapper.java
similarity index 97%
rename from sources/main/java/jdk_internal/bidi/icu/impl/CharacterIteratorWrapper.java
rename to sources/main/java/jdk_internal/icu/impl/CharacterIteratorWrapper.java
index 7d6fcede..72d0a79b 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/CharacterIteratorWrapper.java
+++ b/sources/main/java/jdk_internal/icu/impl/CharacterIteratorWrapper.java
@@ -35,10 +35,10 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import jdk_internal.bidi.CharacterIterator;
-import jdk_internal.bidi.icu.text.UCharacterIterator;
+import jdk_internal.icu.text.UCharacterIterator;
 
 /**
  * This class is a wrapper around CharacterIterator and implements the
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/ICUBinary.java b/sources/main/java/jdk_internal/icu/impl/ICUBinary.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/ICUBinary.java
rename to sources/main/java/jdk_internal/icu/impl/ICUBinary.java
index dbbe0b39..c1a3e472 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/ICUBinary.java
+++ b/sources/main/java/jdk_internal/icu/impl/ICUBinary.java
@@ -30,7 +30,7 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.DataInputStream;
 import java.io.InputStream;
@@ -40,7 +40,7 @@ import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.Arrays;
 
-import jdk_internal.bidi.icu.util.VersionInfo;
+import jdk_internal.icu.util.VersionInfo;
 import net.lax1dude.eaglercraft.v1_8.EagRuntime;
 
 public final class ICUBinary {
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/Norm2AllModes.java b/sources/main/java/jdk_internal/icu/impl/Norm2AllModes.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/Norm2AllModes.java
rename to sources/main/java/jdk_internal/icu/impl/Norm2AllModes.java
index aaabf2f0..58293e43 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/Norm2AllModes.java
+++ b/sources/main/java/jdk_internal/icu/impl/Norm2AllModes.java
@@ -30,11 +30,11 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.IOException;
 
-import jdk_internal.bidi.icu.text.Normalizer2;
+import jdk_internal.icu.text.Normalizer2;
 
 public final class Norm2AllModes {
 	// Public API dispatch via Normalizer2 subclasses -------------------------- ***
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/NormalizerImpl.java b/sources/main/java/jdk_internal/icu/impl/NormalizerImpl.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/NormalizerImpl.java
rename to sources/main/java/jdk_internal/icu/impl/NormalizerImpl.java
index 920aa7a0..00884aa7 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/NormalizerImpl.java
+++ b/sources/main/java/jdk_internal/icu/impl/NormalizerImpl.java
@@ -29,16 +29,16 @@
  *   Corporation and others.  All Rights Reserved.
  *******************************************************************************
  */
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
-import jdk_internal.bidi.icu.lang.UCharacter;
-import jdk_internal.bidi.icu.text.Normalizer2;
-import jdk_internal.bidi.icu.text.UTF16;
-import jdk_internal.bidi.icu.util.CodePointTrie;
-import jdk_internal.bidi.icu.util.VersionInfo;
+import jdk_internal.icu.lang.UCharacter;
+import jdk_internal.icu.text.Normalizer2;
+import jdk_internal.icu.text.UTF16;
+import jdk_internal.icu.util.CodePointTrie;
+import jdk_internal.icu.util.VersionInfo;
 
 // Original filename in ICU4J: Normalizer2Impl.java
 public final class NormalizerImpl {
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/Punycode.java b/sources/main/java/jdk_internal/icu/impl/Punycode.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/Punycode.java
rename to sources/main/java/jdk_internal/icu/impl/Punycode.java
index c6491b0c..5fe701a4 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/Punycode.java
+++ b/sources/main/java/jdk_internal/icu/impl/Punycode.java
@@ -37,12 +37,12 @@
 //      2007-08-14 Martin Buchholz
 //          - remove redundant casts
 //
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.text.ParseException;
 
-import jdk_internal.bidi.icu.lang.UCharacter;
-import jdk_internal.bidi.icu.text.UTF16;
+import jdk_internal.icu.lang.UCharacter;
+import jdk_internal.icu.text.UTF16;
 
 /**
  * Ported code from ICU punycode.c
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/ReplaceableUCharacterIterator.java b/sources/main/java/jdk_internal/icu/impl/ReplaceableUCharacterIterator.java
similarity index 96%
rename from sources/main/java/jdk_internal/bidi/icu/impl/ReplaceableUCharacterIterator.java
rename to sources/main/java/jdk_internal/icu/impl/ReplaceableUCharacterIterator.java
index 3e090726..c1b00bb2 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/ReplaceableUCharacterIterator.java
+++ b/sources/main/java/jdk_internal/icu/impl/ReplaceableUCharacterIterator.java
@@ -35,11 +35,11 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
-import jdk_internal.bidi.icu.text.Replaceable;
-import jdk_internal.bidi.icu.text.ReplaceableString;
-import jdk_internal.bidi.icu.text.UCharacterIterator;
+import jdk_internal.icu.text.Replaceable;
+import jdk_internal.icu.text.ReplaceableString;
+import jdk_internal.icu.text.UCharacterIterator;
 
 /**
  * DLF docs must define behavior when Replaceable is mutated underneath the
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/StringPrepDataReader.java b/sources/main/java/jdk_internal/icu/impl/StringPrepDataReader.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/StringPrepDataReader.java
rename to sources/main/java/jdk_internal/icu/impl/StringPrepDataReader.java
index 98aafc74..149155f0 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/StringPrepDataReader.java
+++ b/sources/main/java/jdk_internal/icu/impl/StringPrepDataReader.java
@@ -39,7 +39,7 @@
 //          - copy this file from icu4jsrc_3_2/src/com/ibm/icu/impl/StringPrepDataReader.java
 //          - move from package com.ibm.icu.impl to package sun.net.idn
 //
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.DataInputStream;
 import java.io.IOException;
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/Trie.java b/sources/main/java/jdk_internal/icu/impl/Trie.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/Trie.java
rename to sources/main/java/jdk_internal/icu/impl/Trie.java
index 2b5038ee..ea48c547 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/Trie.java
+++ b/sources/main/java/jdk_internal/icu/impl/Trie.java
@@ -30,13 +30,13 @@
  ******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.DataInputStream;
 import java.io.InputStream;
 
-import jdk_internal.bidi.icu.lang.UCharacter;
-import jdk_internal.bidi.icu.text.UTF16;
+import jdk_internal.icu.lang.UCharacter;
+import jdk_internal.icu.text.UTF16;
 
 import java.io.IOException;
 
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/Trie2.java b/sources/main/java/jdk_internal/icu/impl/Trie2.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/Trie2.java
rename to sources/main/java/jdk_internal/icu/impl/Trie2.java
index 94c72fcd..15347caf 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/Trie2.java
+++ b/sources/main/java/jdk_internal/icu/impl/Trie2.java
@@ -30,7 +30,7 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/Trie2_16.java b/sources/main/java/jdk_internal/icu/impl/Trie2_16.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/Trie2_16.java
rename to sources/main/java/jdk_internal/icu/impl/Trie2_16.java
index 770754a7..632ac070 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/Trie2_16.java
+++ b/sources/main/java/jdk_internal/icu/impl/Trie2_16.java
@@ -30,7 +30,7 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/UBiDiProps.java b/sources/main/java/jdk_internal/icu/impl/UBiDiProps.java
similarity index 98%
rename from sources/main/java/jdk_internal/bidi/icu/impl/UBiDiProps.java
rename to sources/main/java/jdk_internal/icu/impl/UBiDiProps.java
index b81ec6b7..70331b71 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/UBiDiProps.java
+++ b/sources/main/java/jdk_internal/icu/impl/UBiDiProps.java
@@ -41,11 +41,12 @@
  *   Java port of ubidi_props.h/.c.
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import jdk_internal.bidi.icu.lang.UCharacter;
+
+import jdk_internal.icu.lang.UCharacter;
 
 public final class UBiDiProps {
 	// constructors etc. --------------------------------------------------- ***
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/UCharacterProperty.java b/sources/main/java/jdk_internal/icu/impl/UCharacterProperty.java
similarity index 98%
rename from sources/main/java/jdk_internal/bidi/icu/impl/UCharacterProperty.java
rename to sources/main/java/jdk_internal/icu/impl/UCharacterProperty.java
index 7b77640f..1d838380 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/UCharacterProperty.java
+++ b/sources/main/java/jdk_internal/icu/impl/UCharacterProperty.java
@@ -29,17 +29,17 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Iterator;
 
-import jdk_internal.bidi.icu.lang.UCharacter.HangulSyllableType;
-import jdk_internal.bidi.icu.lang.UCharacter.NumericType;
-import jdk_internal.bidi.icu.text.UTF16;
-import jdk_internal.bidi.icu.text.UnicodeSet;
-import jdk_internal.bidi.icu.util.VersionInfo;
+import jdk_internal.icu.lang.UCharacter.HangulSyllableType;
+import jdk_internal.icu.lang.UCharacter.NumericType;
+import jdk_internal.icu.text.UTF16;
+import jdk_internal.icu.text.UnicodeSet;
+import jdk_internal.icu.util.VersionInfo;
 
 /**
  * <p>
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/UnicodeSetStringSpan.java b/sources/main/java/jdk_internal/icu/impl/UnicodeSetStringSpan.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/impl/UnicodeSetStringSpan.java
rename to sources/main/java/jdk_internal/icu/impl/UnicodeSetStringSpan.java
index c89f8b09..ddde4688 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/UnicodeSetStringSpan.java
+++ b/sources/main/java/jdk_internal/icu/impl/UnicodeSetStringSpan.java
@@ -32,14 +32,14 @@
  ******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.util.ArrayList;
 
-import jdk_internal.bidi.icu.text.UTF16;
-import jdk_internal.bidi.icu.text.UnicodeSet;
-import jdk_internal.bidi.icu.text.UnicodeSet.SpanCondition;
-import jdk_internal.bidi.icu.util.OutputInt;
+import jdk_internal.icu.text.UTF16;
+import jdk_internal.icu.text.UnicodeSet;
+import jdk_internal.icu.text.UnicodeSet.SpanCondition;
+import jdk_internal.icu.util.OutputInt;
 
 /*
  * Implement span() etc. for a set with strings.
diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/Utility.java b/sources/main/java/jdk_internal/icu/impl/Utility.java
similarity index 98%
rename from sources/main/java/jdk_internal/bidi/icu/impl/Utility.java
rename to sources/main/java/jdk_internal/icu/impl/Utility.java
index 74d0852d..c1915b15 100644
--- a/sources/main/java/jdk_internal/bidi/icu/impl/Utility.java
+++ b/sources/main/java/jdk_internal/icu/impl/Utility.java
@@ -29,13 +29,13 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.impl;
+package jdk_internal.icu.impl;
 
 import java.io.IOException;
 import java.util.Locale;
 
-import jdk_internal.bidi.icu.lang.UCharacter;
-import jdk_internal.bidi.icu.text.UTF16;
+import jdk_internal.icu.lang.UCharacter;
+import jdk_internal.icu.text.UTF16;
 
 public final class Utility {
 
diff --git a/sources/main/java/jdk_internal/bidi/icu/lang/UCharacter.java b/sources/main/java/jdk_internal/icu/lang/UCharacter.java
similarity index 98%
rename from sources/main/java/jdk_internal/bidi/icu/lang/UCharacter.java
rename to sources/main/java/jdk_internal/icu/lang/UCharacter.java
index 3c0fce0e..e47952e0 100644
--- a/sources/main/java/jdk_internal/bidi/icu/lang/UCharacter.java
+++ b/sources/main/java/jdk_internal/icu/lang/UCharacter.java
@@ -30,13 +30,13 @@
 *******************************************************************************
 */
 
-package jdk_internal.bidi.icu.lang;
+package jdk_internal.icu.lang;
 
-import jdk_internal.bidi.icu.impl.UBiDiProps;
-import jdk_internal.bidi.icu.impl.UCharacterProperty;
-import jdk_internal.bidi.icu.text.Normalizer2;
-import jdk_internal.bidi.icu.text.UTF16;
-import jdk_internal.bidi.icu.util.VersionInfo;
+import jdk_internal.icu.impl.UBiDiProps;
+import jdk_internal.icu.impl.UCharacterProperty;
+import jdk_internal.icu.text.Normalizer2;
+import jdk_internal.icu.text.UTF16;
+import jdk_internal.icu.util.VersionInfo;
 
 /**
  * <p>
diff --git a/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterDirection.java b/sources/main/java/jdk_internal/icu/lang/UCharacterDirection.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/lang/UCharacterDirection.java
rename to sources/main/java/jdk_internal/icu/lang/UCharacterDirection.java
index 7a3c8035..af9f455d 100644
--- a/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterDirection.java
+++ b/sources/main/java/jdk_internal/icu/lang/UCharacterDirection.java
@@ -35,7 +35,7 @@
 //          - move from package com.ibm.icu.lang to package sun.net.idn
 //
 
-package jdk_internal.bidi.icu.lang;
+package jdk_internal.icu.lang;
 
 /**
  * Enumerated Unicode character linguistic direction constants. Used as return
diff --git a/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterEnums.java b/sources/main/java/jdk_internal/icu/lang/UCharacterEnums.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/lang/UCharacterEnums.java
rename to sources/main/java/jdk_internal/icu/lang/UCharacterEnums.java
index dd09e6f6..1e196dc5 100644
--- a/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterEnums.java
+++ b/sources/main/java/jdk_internal/icu/lang/UCharacterEnums.java
@@ -51,7 +51,7 @@
 //              DIRECTIONALITY_BOUNDARY_NEUTRAL, DIRECTIONALITY_UNDEFINED
 //
 
-package jdk_internal.bidi.icu.lang;
+package jdk_internal.icu.lang;
 
 /**
  * A container for the different 'enumerated types' used by UCharacter.
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/BidiBase.java b/sources/main/java/jdk_internal/icu/text/BidiBase.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/BidiBase.java
rename to sources/main/java/jdk_internal/icu/text/BidiBase.java
index ce33152a..9f4f24dc 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/BidiBase.java
+++ b/sources/main/java/jdk_internal/icu/text/BidiBase.java
@@ -46,7 +46,7 @@
  * fallbacks for unsupported combinations.
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 import java.lang.reflect.Array;
 import java.util.Arrays;
@@ -55,8 +55,8 @@ import jdk_internal.bidi.AttributedCharacterIterator;
 import jdk_internal.bidi.Bidi;
 import jdk_internal.bidi.NumericShaper;
 import jdk_internal.bidi.TextAttribute;
-import jdk_internal.bidi.icu.impl.UBiDiProps;
-import jdk_internal.bidi.icu.lang.UCharacter;
+import jdk_internal.icu.impl.UBiDiProps;
+import jdk_internal.icu.lang.UCharacter;
 
 /**
  *
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/BidiLine.java b/sources/main/java/jdk_internal/icu/text/BidiLine.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/BidiLine.java
rename to sources/main/java/jdk_internal/icu/text/BidiLine.java
index 19d8e73e..56620549 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/BidiLine.java
+++ b/sources/main/java/jdk_internal/icu/text/BidiLine.java
@@ -33,7 +33,7 @@
  * (ported from C code written by Markus W. Scherer)
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 import java.util.Arrays;
 
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/BidiRun.java b/sources/main/java/jdk_internal/icu/text/BidiRun.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/BidiRun.java
rename to sources/main/java/jdk_internal/icu/text/BidiRun.java
index 97b0e810..333af89e 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/BidiRun.java
+++ b/sources/main/java/jdk_internal/icu/text/BidiRun.java
@@ -37,7 +37,7 @@
  * (ported from C code written by Markus W. Scherer)
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 /**
  * A BidiRun represents a sequence of characters at the same embedding level.
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/BidiWriter.java b/sources/main/java/jdk_internal/icu/text/BidiWriter.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/BidiWriter.java
rename to sources/main/java/jdk_internal/icu/text/BidiWriter.java
index e9fa71e6..60339207 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/BidiWriter.java
+++ b/sources/main/java/jdk_internal/icu/text/BidiWriter.java
@@ -33,9 +33,9 @@
  * (ported from C code written by Markus W. Scherer)
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
-import jdk_internal.bidi.icu.lang.UCharacter;
+import jdk_internal.icu.lang.UCharacter;
 
 final class BidiWriter {
 
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/FilteredNormalizer2.java b/sources/main/java/jdk_internal/icu/text/FilteredNormalizer2.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/FilteredNormalizer2.java
rename to sources/main/java/jdk_internal/icu/text/FilteredNormalizer2.java
index 3f9046bb..f3915e57 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/FilteredNormalizer2.java
+++ b/sources/main/java/jdk_internal/icu/text/FilteredNormalizer2.java
@@ -29,7 +29,7 @@
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
 */
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 import java.io.IOException;
 
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/Normalizer2.java b/sources/main/java/jdk_internal/icu/text/Normalizer2.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/Normalizer2.java
rename to sources/main/java/jdk_internal/icu/text/Normalizer2.java
index f833478f..5471fcd1 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/Normalizer2.java
+++ b/sources/main/java/jdk_internal/icu/text/Normalizer2.java
@@ -30,9 +30,9 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
-import jdk_internal.bidi.icu.impl.Norm2AllModes;
+import jdk_internal.icu.impl.Norm2AllModes;
 
 /**
  * Unicode normalization functionality for standard Unicode normalization or for
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/NormalizerBase.java b/sources/main/java/jdk_internal/icu/text/NormalizerBase.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/NormalizerBase.java
rename to sources/main/java/jdk_internal/icu/text/NormalizerBase.java
index 5887aecb..d10b15f0 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/NormalizerBase.java
+++ b/sources/main/java/jdk_internal/icu/text/NormalizerBase.java
@@ -29,11 +29,11 @@
  * others. All Rights Reserved.
  *******************************************************************************
  */
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 import jdk_internal.bidi.CharacterIterator;
 import jdk_internal.bidi.Normalizer;
-import jdk_internal.bidi.icu.impl.Norm2AllModes;
+import jdk_internal.icu.impl.Norm2AllModes;
 
 /**
  * Unicode Normalization
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/Replaceable.java b/sources/main/java/jdk_internal/icu/text/Replaceable.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/Replaceable.java
rename to sources/main/java/jdk_internal/icu/text/Replaceable.java
index 22c7fff6..cd843b17 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/Replaceable.java
+++ b/sources/main/java/jdk_internal/icu/text/Replaceable.java
@@ -35,7 +35,7 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 /**
  * <code>Replaceable</code> is an interface representing a string of characters
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/ReplaceableString.java b/sources/main/java/jdk_internal/icu/text/ReplaceableString.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/ReplaceableString.java
rename to sources/main/java/jdk_internal/icu/text/ReplaceableString.java
index 661eccac..c9b75d6d 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/ReplaceableString.java
+++ b/sources/main/java/jdk_internal/icu/text/ReplaceableString.java
@@ -30,7 +30,7 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 /**
  * <code>ReplaceableString</code> is an adapter class that implements the
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/StringPrep.java b/sources/main/java/jdk_internal/icu/text/StringPrep.java
similarity index 98%
rename from sources/main/java/jdk_internal/bidi/icu/text/StringPrep.java
rename to sources/main/java/jdk_internal/icu/text/StringPrep.java
index da41151a..656c484b 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/StringPrep.java
+++ b/sources/main/java/jdk_internal/icu/text/StringPrep.java
@@ -40,7 +40,7 @@
 //      2007-08-14 Martin Buchholz
 //          - remove redundant casts
 //
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
@@ -50,12 +50,12 @@ import java.io.InputStream;
 import jdk_internal.bidi.Normalizer;
 import jdk_internal.bidi.ParseException;
 import jdk_internal.bidi.SunNormalizer;
-import jdk_internal.bidi.icu.impl.CharTrie;
-import jdk_internal.bidi.icu.impl.StringPrepDataReader;
-import jdk_internal.bidi.icu.impl.Trie;
-import jdk_internal.bidi.icu.lang.UCharacter;
-import jdk_internal.bidi.icu.lang.UCharacterDirection;
-import jdk_internal.bidi.icu.util.VersionInfo;
+import jdk_internal.icu.impl.CharTrie;
+import jdk_internal.icu.impl.StringPrepDataReader;
+import jdk_internal.icu.impl.Trie;
+import jdk_internal.icu.lang.UCharacter;
+import jdk_internal.icu.lang.UCharacterDirection;
+import jdk_internal.icu.util.VersionInfo;
 
 /**
  * StringPrep API implements the StingPrep framework as described by
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/UCharacterIterator.java b/sources/main/java/jdk_internal/icu/text/UCharacterIterator.java
similarity index 98%
rename from sources/main/java/jdk_internal/bidi/icu/text/UCharacterIterator.java
rename to sources/main/java/jdk_internal/icu/text/UCharacterIterator.java
index 656b4df5..55031d63 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/UCharacterIterator.java
+++ b/sources/main/java/jdk_internal/icu/text/UCharacterIterator.java
@@ -30,12 +30,12 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 import jdk_internal.bidi.CharacterIterator;
-import jdk_internal.bidi.icu.impl.CharacterIteratorWrapper;
-import jdk_internal.bidi.icu.impl.ReplaceableUCharacterIterator;
-import jdk_internal.bidi.icu.impl.UCharacterProperty;
+import jdk_internal.icu.impl.CharacterIteratorWrapper;
+import jdk_internal.icu.impl.ReplaceableUCharacterIterator;
+import jdk_internal.icu.impl.UCharacterProperty;
 
 /**
  * Abstract class that defines an API for iteration on text objects.This is an
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/UTF16.java b/sources/main/java/jdk_internal/icu/text/UTF16.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/UTF16.java
rename to sources/main/java/jdk_internal/icu/text/UTF16.java
index 5a39fedf..3efde5af 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/UTF16.java
+++ b/sources/main/java/jdk_internal/icu/text/UTF16.java
@@ -29,9 +29,9 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
-import jdk_internal.bidi.icu.impl.UCharacterProperty;
+import jdk_internal.icu.impl.UCharacterProperty;
 
 /**
  * <p>
diff --git a/sources/main/java/jdk_internal/bidi/icu/text/UnicodeSet.java b/sources/main/java/jdk_internal/icu/text/UnicodeSet.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/text/UnicodeSet.java
rename to sources/main/java/jdk_internal/icu/text/UnicodeSet.java
index 5797c6be..cd46cdc4 100644
--- a/sources/main/java/jdk_internal/bidi/icu/text/UnicodeSet.java
+++ b/sources/main/java/jdk_internal/icu/text/UnicodeSet.java
@@ -29,19 +29,19 @@
  * others. All Rights Reserved.
  *******************************************************************************
  */
-package jdk_internal.bidi.icu.text;
+package jdk_internal.icu.text;
 
 import java.text.ParsePosition;
 import java.util.ArrayList;
 import java.util.TreeSet;
 
-import jdk_internal.bidi.icu.impl.BMPSet;
-import jdk_internal.bidi.icu.impl.UCharacterProperty;
-import jdk_internal.bidi.icu.impl.UnicodeSetStringSpan;
-import jdk_internal.bidi.icu.impl.Utility;
-import jdk_internal.bidi.icu.lang.UCharacter;
-import jdk_internal.bidi.icu.util.OutputInt;
-import jdk_internal.bidi.icu.util.VersionInfo;
+import jdk_internal.icu.impl.BMPSet;
+import jdk_internal.icu.impl.UCharacterProperty;
+import jdk_internal.icu.impl.UnicodeSetStringSpan;
+import jdk_internal.icu.impl.Utility;
+import jdk_internal.icu.lang.UCharacter;
+import jdk_internal.icu.util.OutputInt;
+import jdk_internal.icu.util.VersionInfo;
 
 /**
  * A mutable set of Unicode characters and multicharacter strings. Objects of
diff --git a/sources/main/java/jdk_internal/bidi/icu/util/CodePointMap.java b/sources/main/java/jdk_internal/icu/util/CodePointMap.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/util/CodePointMap.java
rename to sources/main/java/jdk_internal/icu/util/CodePointMap.java
index 358b631b..e7b0c4ed 100644
--- a/sources/main/java/jdk_internal/bidi/icu/util/CodePointMap.java
+++ b/sources/main/java/jdk_internal/icu/util/CodePointMap.java
@@ -27,7 +27,7 @@
 
 // created: 2018may10 Markus W. Scherer
 
-package jdk_internal.bidi.icu.util;
+package jdk_internal.icu.util;
 
 import java.util.Iterator;
 import java.util.NoSuchElementException;
diff --git a/sources/main/java/jdk_internal/bidi/icu/util/CodePointTrie.java b/sources/main/java/jdk_internal/icu/util/CodePointTrie.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/util/CodePointTrie.java
rename to sources/main/java/jdk_internal/icu/util/CodePointTrie.java
index e84c2d0e..30f1bb76 100644
--- a/sources/main/java/jdk_internal/bidi/icu/util/CodePointTrie.java
+++ b/sources/main/java/jdk_internal/icu/util/CodePointTrie.java
@@ -27,9 +27,9 @@
 
 // created: 2018may04 Markus W. Scherer
 
-package jdk_internal.bidi.icu.util;
+package jdk_internal.icu.util;
 
-import static jdk_internal.bidi.icu.impl.NormalizerImpl.UTF16Plus;
+import static jdk_internal.icu.impl.NormalizerImpl.UTF16Plus;
 
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -38,7 +38,7 @@ import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
-import jdk_internal.bidi.icu.impl.ICUBinary;
+import jdk_internal.icu.impl.ICUBinary;
 
 /**
  * Immutable Unicode code point trie. Fast, reasonably compact, map from Unicode
diff --git a/sources/main/java/jdk_internal/bidi/icu/util/OutputInt.java b/sources/main/java/jdk_internal/icu/util/OutputInt.java
similarity index 98%
rename from sources/main/java/jdk_internal/bidi/icu/util/OutputInt.java
rename to sources/main/java/jdk_internal/icu/util/OutputInt.java
index 433bf28a..973e4168 100644
--- a/sources/main/java/jdk_internal/bidi/icu/util/OutputInt.java
+++ b/sources/main/java/jdk_internal/icu/util/OutputInt.java
@@ -29,7 +29,7 @@
  * others. All Rights Reserved.
  *******************************************************************************
  */
-package jdk_internal.bidi.icu.util;
+package jdk_internal.icu.util;
 
 /**
  * Simple struct-like class for int output parameters. Like
diff --git a/sources/main/java/jdk_internal/bidi/icu/util/VersionInfo.java b/sources/main/java/jdk_internal/icu/util/VersionInfo.java
similarity index 99%
rename from sources/main/java/jdk_internal/bidi/icu/util/VersionInfo.java
rename to sources/main/java/jdk_internal/icu/util/VersionInfo.java
index d6abf225..06724a83 100644
--- a/sources/main/java/jdk_internal/bidi/icu/util/VersionInfo.java
+++ b/sources/main/java/jdk_internal/icu/util/VersionInfo.java
@@ -34,7 +34,7 @@
  *******************************************************************************
  */
 
-package jdk_internal.bidi.icu.util;
+package jdk_internal.icu.util;
 
 import java.util.HashMap;
 
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerProperties.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerProperties.java
new file mode 100644
index 00000000..fc4311ef
--- /dev/null
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerProperties.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+package net.lax1dude.eaglercraft.v1_8;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.Properties;
+
+public class EaglerProperties extends Properties {
+
+	public void load(Reader reader) throws IOException {
+		load0(new LineReader(reader));
+	}
+
+	public void load(InputStream inStream) throws IOException {
+		load0(new LineReader(inStream));
+	}
+
+	private void load0(LineReader lr) throws IOException {
+		StringBuilder outBuffer = new StringBuilder();
+		int limit;
+		int keyLen;
+		int valueStart;
+		boolean hasSep;
+		boolean precedingBackslash;
+		boolean first = true;
+
+		while ((limit = lr.readLine()) >= 0) {
+			keyLen = 0;
+			valueStart = limit;
+			hasSep = false;
+			if(first && limit > 0) {
+				if(lr.lineBuf[0] == 65279) {
+					keyLen = 1;
+				}
+			}
+			first = false;
+
+			// System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
+			precedingBackslash = false;
+			while (keyLen < limit) {
+				char c = lr.lineBuf[keyLen];
+				// need check if escaped.
+				if ((c == '=' || c == ':') && !precedingBackslash) {
+					valueStart = keyLen + 1;
+					hasSep = true;
+					break;
+				} else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
+					valueStart = keyLen + 1;
+					break;
+				}
+				if (c == '\\') {
+					precedingBackslash = !precedingBackslash;
+				} else {
+					precedingBackslash = false;
+				}
+				keyLen++;
+			}
+			while (valueStart < limit) {
+				char c = lr.lineBuf[valueStart];
+				if (c != ' ' && c != '\t' && c != '\f') {
+					if (!hasSep && (c == '=' || c == ':')) {
+						hasSep = true;
+					} else {
+						break;
+					}
+				}
+				valueStart++;
+			}
+			String key = loadConvert(lr.lineBuf, 0, keyLen, outBuffer);
+			String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, outBuffer);
+			put(key, value);
+		}
+	}
+
+	/*
+	 * Read in a "logical line" from an InputStream/Reader, skip all comment and
+	 * blank lines and filter out those leading whitespace characters (\u0020,
+	 * \u0009 and \u000c) from the beginning of a "natural line". Method returns the
+	 * char length of the "logical line" and stores the line in "lineBuf".
+	 */
+	private static class LineReader {
+		LineReader(InputStream inStream) {
+			this.inStream = inStream;
+			inByteBuf = new byte[8192];
+		}
+
+		LineReader(Reader reader) {
+			this.reader = reader;
+			inCharBuf = new char[8192];
+		}
+
+		char[] lineBuf = new char[1024];
+		private byte[] inByteBuf;
+		private char[] inCharBuf;
+		private int inLimit = 0;
+		private int inOff = 0;
+		private InputStream inStream;
+		private Reader reader;
+
+		int readLine() throws IOException {
+			// use locals to optimize for interpreted performance
+			int len = 0;
+			int off = inOff;
+			int limit = inLimit;
+
+			boolean skipWhiteSpace = true;
+			boolean appendedLineBegin = false;
+			boolean precedingBackslash = false;
+			boolean fromStream = inStream != null;
+			byte[] byteBuf = inByteBuf;
+			char[] charBuf = inCharBuf;
+			char[] lineBuf = this.lineBuf;
+			char c;
+
+			while (true) {
+				if (off >= limit) {
+					inLimit = limit = fromStream ? inStream.read(byteBuf) : reader.read(charBuf);
+					if (limit <= 0) {
+						if (len == 0) {
+							return -1;
+						}
+						return precedingBackslash ? len - 1 : len;
+					}
+					off = 0;
+				}
+
+				// (char)(byte & 0xFF) is equivalent to calling a ISO8859-1 decoder.
+				c = (fromStream) ? (char) (byteBuf[off++] & 0xFF) : charBuf[off++];
+
+				if (skipWhiteSpace) {
+					if (c == ' ' || c == '\t' || c == '\f') {
+						continue;
+					}
+					if (!appendedLineBegin && (c == '\r' || c == '\n')) {
+						continue;
+					}
+					skipWhiteSpace = false;
+					appendedLineBegin = false;
+
+				}
+				if (len == 0) { // Still on a new logical line
+					if (c == '#' || c == '!') {
+						// Comment, quickly consume the rest of the line
+
+						// When checking for new line characters a range check,
+						// starting with the higher bound ('\r') means one less
+						// branch in the common case.
+						commentLoop: while (true) {
+							if (fromStream) {
+								byte b;
+								while (off < limit) {
+									b = byteBuf[off++];
+									if (b <= '\r' && (b == '\r' || b == '\n'))
+										break commentLoop;
+								}
+								if (off == limit) {
+									inLimit = limit = inStream.read(byteBuf);
+									if (limit <= 0) { // EOF
+										return -1;
+									}
+									off = 0;
+								}
+							} else {
+								while (off < limit) {
+									c = charBuf[off++];
+									if (c <= '\r' && (c == '\r' || c == '\n'))
+										break commentLoop;
+								}
+								if (off == limit) {
+									inLimit = limit = reader.read(charBuf);
+									if (limit <= 0) { // EOF
+										return -1;
+									}
+									off = 0;
+								}
+							}
+						}
+						skipWhiteSpace = true;
+						continue;
+					}
+				}
+
+				if (c != '\n' && c != '\r') {
+					lineBuf[len++] = c;
+					if (len == lineBuf.length) {
+						lineBuf = new char[len + (len << 1)];
+						System.arraycopy(this.lineBuf, 0, lineBuf, 0, len);
+						this.lineBuf = lineBuf;
+					}
+					// flip the preceding backslash flag
+					precedingBackslash = (c == '\\') ? !precedingBackslash : false;
+				} else {
+					// reached EOL
+					if (len == 0) {
+						skipWhiteSpace = true;
+						continue;
+					}
+					if (off >= limit) {
+						inLimit = limit = fromStream ? inStream.read(byteBuf) : reader.read(charBuf);
+						off = 0;
+						if (limit <= 0) { // EOF
+							return precedingBackslash ? len - 1 : len;
+						}
+					}
+					if (precedingBackslash) {
+						// backslash at EOL is not part of the line
+						len -= 1;
+						// skip leading whitespace characters in the following line
+						skipWhiteSpace = true;
+						appendedLineBegin = true;
+						precedingBackslash = false;
+						// take care not to include any subsequent \n
+						if (c == '\r') {
+							if (fromStream) {
+								if (byteBuf[off] == '\n') {
+									off++;
+								}
+							} else {
+								if (charBuf[off] == '\n') {
+									off++;
+								}
+							}
+						}
+					} else {
+						inOff = off;
+						return len;
+					}
+				}
+			}
+		}
+	}
+
+	/*
+	 * Converts encoded &#92;uxxxx to unicode chars and changes special saved chars
+	 * to their original forms
+	 */
+	private String loadConvert(char[] in, int off, int len, StringBuilder out) {
+		char aChar;
+		int end = off + len;
+		int start = off;
+		while (off < end) {
+			aChar = in[off++];
+			if (aChar == '\\') {
+				break;
+			}
+		}
+		if (off == end) { // No backslash
+			return new String(in, start, len);
+		}
+
+		// backslash found at off - 1, reset the shared buffer, rewind offset
+		out.setLength(0);
+		off--;
+		out.append(in, start, off - start);
+
+		while (off < end) {
+			aChar = in[off++];
+			if (aChar == '\\') {
+				// No need to bounds check since LineReader::readLine excludes
+				// unescaped \s at the end of the line
+				aChar = in[off++];
+				if (aChar == 'u') {
+					// Read the xxxx
+					if (off > end - 4)
+						throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
+					int value = 0;
+					for (int i = 0; i < 4; i++) {
+						aChar = in[off++];
+						switch (aChar) {
+						case '0':
+						case '1':
+						case '2':
+						case '3':
+						case '4':
+						case '5':
+						case '6':
+						case '7':
+						case '8':
+						case '9':
+							value = (value << 4) + aChar - '0';
+							break;
+						case 'a':
+						case 'b':
+						case 'c':
+						case 'd':
+						case 'e':
+						case 'f':
+							value = (value << 4) + 10 + aChar - 'a';
+							break;
+						case 'A':
+						case 'B':
+						case 'C':
+						case 'D':
+						case 'E':
+						case 'F':
+							value = (value << 4) + 10 + aChar - 'A';
+							break;
+						default:
+							throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
+						};
+					}
+					out.append((char) value);
+				} else {
+					if (aChar == 't')
+						aChar = '\t';
+					else if (aChar == 'r')
+						aChar = '\r';
+					else if (aChar == 'n')
+						aChar = '\n';
+					else if (aChar == 'f')
+						aChar = '\f';
+					out.append(aChar);
+				}
+			} else {
+				out.append(aChar);
+			}
+		}
+		return out.toString();
+	}
+
+}
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java
index 6d00bc72..bb1c3c03 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java
@@ -10,7 +10,7 @@ public class EaglercraftVersion {
 	/// Customize these to fit your fork:
 	
 	public static final String projectForkName = "EaglercraftX";
-	public static final String projectForkVersion = "u47";
+	public static final String projectForkVersion = "u48";
 	public static final String projectForkVendor = "lax1dude";
 	
 	public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8";
@@ -20,20 +20,20 @@ public class EaglercraftVersion {
 	public static final String projectOriginName = "EaglercraftX";
 	public static final String projectOriginAuthor = "lax1dude";
 	public static final String projectOriginRevision = "1.8";
-	public static final String projectOriginVersion = "u47";
+	public static final String projectOriginVersion = "u48";
 	
 	public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace
 	
 	// EPK Version Identifier
 	
-	public static final String EPKVersionIdentifier = "u47"; // Set to null to disable EPK version check
+	public static final String EPKVersionIdentifier = "u48"; // Set to null to disable EPK version check
 	
 	// Updating configuration
 	
 	public static final boolean enableUpdateService = true;
 
 	public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client";
-	public static final int updateBundlePackageVersionInt = 47;
+	public static final int updateBundlePackageVersionInt = 48;
 
 	public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName;
 
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/IOUtils.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/IOUtils.java
index 83655734..0e4191fd 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/IOUtils.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/IOUtils.java
@@ -6,6 +6,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -64,7 +65,12 @@ public class IOUtils {
 				while((s = rd.readLine()) != null) {
 					b.append(s).append('\n');
 				}
-				return b.toString();
+				// Handle BOM
+				if(c == StandardCharsets.UTF_8 && b.length() > 0 && b.charAt(0) == 65279) {
+					return b.substring(1, b.length());
+				}else {
+					return b.toString();
+				}
 			}finally {
 				is.close();
 			}
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/ITextureGL.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/ITextureGL.java
index c073a19e..669f9ddb 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/ITextureGL.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/ITextureGL.java
@@ -17,4 +17,10 @@ package net.lax1dude.eaglercraft.v1_8.internal;
  */
 public interface ITextureGL extends IObjectGL {
 
+	void setCacheSize(int w, int h);
+
+	int getWidth();
+
+	int getHeight();
+
 }
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java
index aecdb25d..a3af8354 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java
@@ -3,6 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.minecraft;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -19,6 +20,7 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
 import net.lax1dude.eaglercraft.v1_8.ArrayUtils;
+import net.lax1dude.eaglercraft.v1_8.EagRuntime;
 import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
 import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest;
 import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
@@ -28,7 +30,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
 import net.minecraft.client.resources.AbstractResourcePack;
 
 /**
- * Copyright (c) 2024 lax1dude. All Rights Reserved.
+ * Copyright (c) 2024-2025 lax1dude. All Rights Reserved.
  * 
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -70,6 +72,80 @@ public class EaglerFolderResourcePack extends AbstractResourcePack {
 		this.prefix = prefix;
 		this.domains = domains;
 		this.timestamp = timestamp;
+		this.resourceIndex = new ResourceIndex() {
+
+			@Override
+			protected Collection<String> getPropertiesFiles0() {
+				VFile2 file = new VFile2(prefix, resourcePackFile, "assets/minecraft/optifine/_property_files_index.json");
+				String str = file.getAllChars();
+				Collection<String> propertyFiles = null;
+				if(str != null) {
+					propertyFiles = loadPropertyFileList(str);
+					if(propertyFiles != null) {
+						return propertyFiles;
+					}
+				}
+				VFile2 mcDir = new VFile2(prefix, resourcePackFile, "assets/minecraft");
+				int pfxLen = mcDir.getPath().length() + 1;
+				List<String> philes = mcDir.listFilenames(true);
+				propertyFiles = new ArrayList<>();
+				JSONArray arr = new JSONArray();
+				for(int i = 0, l = philes.size(); i < l; ++i) {
+					String name = philes.get(i);
+					if(name.length() > pfxLen && name.endsWith(".properties")) {
+						name = name.substring(pfxLen);
+						propertyFiles.add(name);
+						arr.put(name);
+					}
+				}
+				JSONObject json = new JSONObject();
+				json.put("propertyFiles", arr);
+				file.setAllChars(json.toString());
+				return propertyFiles;
+			}
+
+			@Override
+			protected Collection<String> getCITPotionsFiles0() {
+				VFile2 file = new VFile2(prefix, resourcePackFile, "assets/minecraft/mcpatcher/cit/potion/_potions_files_index.json");
+				String str = file.getAllChars();
+				Collection<String> propertyFiles = null;
+				if(str != null) {
+					propertyFiles = loadPotionsFileList(str);
+					if(propertyFiles != null) {
+						return propertyFiles;
+					}
+				}
+				VFile2 mcDir = new VFile2(prefix, resourcePackFile, "assets/minecraft/mcpatcher/cit/potion");
+				int pfxLen = mcDir.getPath().length() - 20;
+				List<String> philes = mcDir.listFilenames(true);
+				propertyFiles = new ArrayList<>();
+				JSONArray arr = new JSONArray();
+				for(int i = 0, l = philes.size(); i < l; ++i) {
+					String name = philes.get(i);
+					if(name.length() > pfxLen && name.endsWith(".png")) {
+						name = name.substring(pfxLen);
+						propertyFiles.add(name);
+						arr.put(name);
+					}
+				}
+				mcDir = new VFile2(prefix, resourcePackFile, "assets/minecraft/optifine/cit/potion");
+				pfxLen = mcDir.getPath().length() - 19;
+				philes = mcDir.listFilenames(true);
+				for(int i = 0, l = philes.size(); i < l; ++i) {
+					String name = philes.get(i);
+					if(name.length() > pfxLen && name.endsWith(".png")) {
+						name = name.substring(pfxLen);
+						propertyFiles.add(name);
+						arr.put(name);
+					}
+				}
+				JSONObject json = new JSONObject();
+				json.put("potionsFiles", arr);
+				file.setAllChars(json.toString());
+				return propertyFiles;
+			}
+
+		};
 	}
 
 	@Override
@@ -177,6 +253,8 @@ public class EaglerFolderResourcePack extends AbstractResourcePack {
 		}
 		
 		Set<String> domainsList = Sets.newHashSet();
+		JSONArray propertyFiles = new JSONArray();
+		JSONArray potionsFiles = new JSONArray();
 		String fn;
 		for(int i = 0, l = fileNames.size(); i < l; ++i) {
 			fn = fileNames.get(i);
@@ -184,7 +262,18 @@ public class EaglerFolderResourcePack extends AbstractResourcePack {
 				fn = fn.substring(prefixLen + 7);
 				int j = fn.indexOf('/');
 				if(j != -1) {
-					domainsList.add(fn.substring(0, j));
+					String dm = fn.substring(0, j);
+					domainsList.add(dm);
+					if("minecraft".equals(dm)) {
+						if(fn.endsWith(".properties")) {
+							propertyFiles.put(fn.substring(10));
+						}else if((fn.startsWith("minecraft/mcpatcher/cit/potion/")
+								|| fn.startsWith("minecraft/mcpatcher/cit/Potion/")) && fn.endsWith(".png")) {
+							potionsFiles.put(fn.substring(10));
+						}else if(fn.startsWith("minecraft/optifine/cit/potion/") && fn.endsWith(".png")) {
+							potionsFiles.put(fn.substring(10));
+						}
+					}
 				}
 			}
 		}
@@ -234,9 +323,19 @@ public class EaglerFolderResourcePack extends AbstractResourcePack {
 			}
 			throw ex;
 		}
-		
+
 		logger.info("Updating manifest...");
 		
+		JSONObject json = new JSONObject();
+		json.put("propertyFiles", propertyFiles);
+		(new VFile2(prefix, folderName, "assets/minecraft/optifine/_property_files_index.json"))
+				.setAllChars(json.toString());
+		
+		json = new JSONObject();
+		json.put("potionsFiles", propertyFiles);
+		(new VFile2(prefix, folderName, "assets/minecraft/mcpatcher/cit/potion/_potions_files_index.json"))
+				.setAllChars(json.toString());
+		
 		VFile2 manifestFile = new VFile2(prefix, "manifest.json");
 		String str = manifestFile.getAllChars();
 		JSONArray arr = null;
@@ -363,4 +462,37 @@ public class EaglerFolderResourcePack extends AbstractResourcePack {
 			}
 		}
 	}
+
+	public static Collection<String> loadPropertyFileList(String str) {
+		try {
+			JSONObject json = new JSONObject(str);
+			JSONArray arr = json.getJSONArray("propertyFiles");
+			int l = arr.length();
+			Collection<String> ret = new ArrayList<>(l);
+			for(int i = 0; i < l; ++i) {
+				ret.add(arr.getString(i));
+			}
+			return ret;
+		}catch(JSONException ex) {
+			EagRuntime.debugPrintStackTrace(ex);
+			return null;
+		}
+	}
+
+	public static Collection<String> loadPotionsFileList(String str) {
+		try {
+			JSONObject json = new JSONObject(str);
+			JSONArray arr = json.getJSONArray("potionsFiles");
+			int l = arr.length();
+			Collection<String> ret = new ArrayList<>(l);
+			for(int i = 0; i < l; ++i) {
+				ret.add(arr.getString(i));
+			}
+			return ret;
+		}catch(JSONException ex) {
+			EagRuntime.debugPrintStackTrace(ex);
+			return null;
+		}
+	}
+
 }
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java
index 9fe6a8c7..7a08b2be 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java
@@ -8,10 +8,10 @@ import com.carrotsearch.hppc.cursors.IntCursor;
 import com.google.common.collect.Lists;
 
 import net.lax1dude.eaglercraft.v1_8.HString;
-import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL;
 import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
 import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
 import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
+import net.minecraft.client.Minecraft;
 import net.minecraft.client.renderer.texture.TextureClock;
 import net.minecraft.client.renderer.texture.TextureCompass;
 import net.minecraft.client.renderer.texture.TextureUtil;
@@ -21,9 +21,10 @@ import net.minecraft.crash.CrashReport;
 import net.minecraft.crash.CrashReportCategory;
 import net.minecraft.util.ReportedException;
 import net.minecraft.util.ResourceLocation;
+import net.optifine.util.CounterInt;
 
 /**
- * Copyright (c) 2022 lax1dude. All Rights Reserved.
+ * Copyright (c) 2022-2025 lax1dude. All Rights Reserved.
  * 
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -56,11 +57,14 @@ public class EaglerTextureAtlasSprite {
 	protected float maxV;
 	protected int frameCounter;
 	protected int tickCounter;
+    private int indexInMap = -1;
 	protected static String locationNameClock = "builtin/clock";
 	protected static String locationNameCompass = "builtin/compass";
 
 	protected TextureAnimationCache animationCache = null;
 
+	public String optifineBaseTextureName = null;
+
 	public EaglerTextureAtlasSprite(String spriteName) {
 		this.iconName = spriteName;
 	}
@@ -101,6 +105,9 @@ public class EaglerTextureAtlasSprite {
 		this.maxU = atlasSpirit.maxU;
 		this.minV = atlasSpirit.minV;
 		this.maxV = atlasSpirit.maxV;
+		if (atlasSpirit != Minecraft.getMinecraft().getTextureMapBlocks().getMissingSprite()) {
+			this.indexInMap = atlasSpirit.indexInMap;
+		}
 	}
 
 	public int getOriginX() {
@@ -149,7 +156,13 @@ public class EaglerTextureAtlasSprite {
 		return this.iconName;
 	}
 
-	public void updateAnimation(IFramebufferGL[] copyColorFramebuffer) {
+	protected static interface IAnimCopyFunction {
+		void updateAnimation(int mapWidth, int mapHeight, int mapLevel);
+	}
+
+	protected IAnimCopyFunction currentAnimUpdater = null;
+
+	public void updateAnimation() {
 		if(animationCache == null) {
 			throw new IllegalStateException("Animation cache for '" + this.iconName + "' was never baked!");
 		}
@@ -162,7 +175,12 @@ public class EaglerTextureAtlasSprite {
 			this.tickCounter = 0;
 			int k = this.animationMetadata.getFrameIndex(this.frameCounter);
 			if (i != k && k >= 0 && k < this.framesTextureData.size()) {
-				animationCache.copyFrameLevelsToTex2D(k, this.originX, this.originY, this.width, this.height, copyColorFramebuffer);
+				currentAnimUpdater = (mapWidth, mapHeight, mapLevel) -> {
+					animationCache.copyFrameToTex2D(k, mapLevel, this.originX >> mapLevel, this.originY >> mapLevel,
+							this.width >> mapLevel, this.height >> mapLevel, mapWidth, mapHeight);
+				};
+			}else {
+				currentAnimUpdater = null;
 			}
 		} else if (this.animationMetadata.isInterpolate()) {
 			float f = 1.0f - (float) this.tickCounter / (float) this.animationMetadata.getFrameTimeSingle(this.frameCounter);
@@ -171,8 +189,22 @@ public class EaglerTextureAtlasSprite {
 					: this.animationMetadata.getFrameCount();
 			int k = this.animationMetadata.getFrameIndex((this.frameCounter + 1) % j);
 			if (i != k && k >= 0 && k < this.framesTextureData.size()) {
-				animationCache.copyInterpolatedFrameLevelsToTex2D(i, k, f, this.originX, this.originY, this.width, this.height, copyColorFramebuffer);
+				currentAnimUpdater = (mapWidth, mapHeight, mapLevel) -> {
+					animationCache.copyInterpolatedFrameToTex2D(i, k, f, mapLevel, this.originX >> mapLevel,
+							this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+							mapHeight);
+				};
+			}else {
+				currentAnimUpdater = null;
 			}
+		} else {
+			currentAnimUpdater = null;
+		}
+	}
+
+	public void copyAnimationFrame(int mapWidth, int mapHeight, int mapLevel) {
+		if(currentAnimUpdater != null) {
+			currentAnimUpdater.updateAnimation(mapWidth, mapHeight, mapLevel);
 		}
 	}
 
@@ -367,7 +399,7 @@ public class EaglerTextureAtlasSprite {
 		}
 	}
 
-	public void updateAnimationPBR(IFramebufferGL[] copyColorFramebuffer, IFramebufferGL[] copyMaterialFramebuffer, int materialTexOffset) {
+	public void updateAnimationPBR() {
 		Throwable t = new UnsupportedOperationException("PBR is not enabled");
 		try {
 			throw t;
@@ -375,4 +407,37 @@ public class EaglerTextureAtlasSprite {
 			logger.error(t);
 		}
 	}
+
+	public void copyAnimationFramePBR(int pass, int width, int height, int level) {
+		Throwable t = new UnsupportedOperationException("PBR is not enabled");
+		try {
+			throw t;
+		}catch(Throwable tt) {
+			logger.error(t);
+		}
+	}
+
+	public int getIndexInMap() {
+		return this.indexInMap;
+	}
+
+	public void setIndexInMap(int p_setIndexInMap_1_) {
+		this.indexInMap = p_setIndexInMap_1_;
+	}
+
+	public void updateIndexInMap(CounterInt p_updateIndexInMap_1_) {
+		if (this.indexInMap < 0) {
+			this.indexInMap = p_updateIndexInMap_1_.nextValue();
+		}
+	}
+
+	public double getSpriteU16(float p_getSpriteU16_1_) {
+		float f = this.maxU - this.minU;
+		return (double) ((p_getSpriteU16_1_ - this.minU) / f * 16.0F);
+	}
+
+	public double getSpriteV16(float p_getSpriteV16_1_) {
+		float f = this.maxV - this.minV;
+		return (double) ((p_getSpriteV16_1_ - this.minV) / f * 16.0F);
+	}
 }
\ No newline at end of file
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/ResourceIndex.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/ResourceIndex.java
new file mode 100644
index 00000000..c581f30d
--- /dev/null
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/ResourceIndex.java
@@ -0,0 +1,43 @@
+package net.lax1dude.eaglercraft.v1_8.minecraft;
+
+import java.util.Collection;
+
+/**
+ * Copyright (c) 2025 lax1dude. All Rights Reserved.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */
+public abstract class ResourceIndex {
+
+	protected Collection<String> propertiesCache = null;
+	protected Collection<String> citPotionsCache = null;
+
+	public Collection<String> getPropertiesFiles() {
+		if(propertiesCache == null) {
+			propertiesCache = getPropertiesFiles0();
+		}
+		return propertiesCache;
+	}
+
+	protected abstract Collection<String> getPropertiesFiles0();
+
+	public Collection<String> getCITPotionsFiles() {
+		if(citPotionsCache == null) {
+			citPotionsCache = getCITPotionsFiles0();
+		}
+		return citPotionsCache;
+	}
+
+	protected abstract Collection<String> getCITPotionsFiles0();
+
+}
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/TextureAnimationCache.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/TextureAnimationCache.java
index b20d3fc0..5caf01bb 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/TextureAnimationCache.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/TextureAnimationCache.java
@@ -1,22 +1,19 @@
 package net.lax1dude.eaglercraft.v1_8.minecraft;
 
-import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
 import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
 
 import java.util.List;
 
 import net.lax1dude.eaglercraft.v1_8.EagRuntime;
-import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL;
 import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
 import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
 import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
 import net.lax1dude.eaglercraft.v1_8.opengl.SpriteLevelMixer;
 import net.lax1dude.eaglercraft.v1_8.opengl.TextureCopyUtil;
-import net.lax1dude.eaglercraft.v1_8.vector.Matrix3f;
 import net.minecraft.client.renderer.GLAllocation;
 
 /**
- * Copyright (c) 2022 lax1dude. All Rights Reserved.
+ * Copyright (c) 2022-2025 lax1dude. All Rights Reserved.
  * 
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -40,8 +37,6 @@ public class TextureAnimationCache {
 
 	private int[] cacheTextures = null;
 
-	public static final int _GL_FRAMEBUFFER = 0x8D40;
-
 	public TextureAnimationCache(int width, int height, int mipLevels) {
 		this.width = width;
 		this.height = height;
@@ -105,76 +100,40 @@ public class TextureAnimationCache {
 		}
 	}
 
-	public void copyFrameLevelsToTex2D(int animationFrame, int dx, int dy, int w, int h, IFramebufferGL[] dstFramebuffers) {
-		copyFrameLevelsToTex2D(animationFrame, mipLevels, dx, dy, w, h, dstFramebuffers);
-	}
-
-	/**
-	 * WARNING: call <code>_wglBindFramebuffer(_GL_FRAMEBUFFER, null);</code> when complete
-	 */
-	public void copyFrameLevelsToTex2D(int animationFrame, int levels, int dx, int dy, int w, int h, IFramebufferGL[] dstFramebuffers) {
-		for(int i = 0; i < levels; ++i) {
-			_wglBindFramebuffer(_GL_FRAMEBUFFER, dstFramebuffers[i]);
-			copyFrameToTex2D(animationFrame, i, dx >> i, dy >> i, w >> i, h >> i);
-		}
-	}
-
-	public void copyFrameToTex2D(int animationFrame, int level, int dx, int dy, int w, int h) {
+	public void copyFrameToTex2D(int animationFrame, int level, int dx, int dy, int w, int h, int mapWidth, int mapHeight) {
 		if(cacheTextures == null) {
 			throw new IllegalStateException("Cannot copy from uninitialized TextureAnimationCache");
 		}
 		GlStateManager.disableBlend();
-		GlStateManager.disableAlpha();
 		GlStateManager.bindTexture(cacheTextures[level]);
 		TextureCopyUtil.srcSize(width >> level, (height >> level) * frameCount);
-		TextureCopyUtil.blitTextureUsingViewports(0, h * animationFrame, dx, dy, w, h);
-	}
-
-	public void copyInterpolatedFrameLevelsToTex2D(int animationFrameFrom, int animationFrameTo, float factor, int dx,
-			int dy, int w, int h, IFramebufferGL[] dstFramebuffers) {
-		copyInterpolatedFrameLevelsToTex2D(animationFrameFrom, animationFrameTo, factor, mipLevels, dx, dy, w, h, dstFramebuffers);
-	}
-
-	/**
-	 * WARNING: call <code>_wglBindFramebuffer(_GL_FRAMEBUFFER, null);</code> when complete
-	 */
-	public void copyInterpolatedFrameLevelsToTex2D(int animationFrameFrom, int animationFrameTo, float factor,
-			int levels, int dx, int dy, int w, int h, IFramebufferGL[] dstFramebuffers) {
-		for(int i = 0; i < levels; ++i) {
-			_wglBindFramebuffer(_GL_FRAMEBUFFER, dstFramebuffers[i]);
-			copyInterpolatedFrameToTex2D(animationFrameFrom, animationFrameTo, factor, i, dx >> i, dy >> i, w >> i, h >> i);
-		}
+		TextureCopyUtil.dstSize(mapWidth, mapHeight);
+		TextureCopyUtil.blitTexture(0, h * animationFrame, dx, dy, w, h);
 	}
 
 	public void copyInterpolatedFrameToTex2D(int animationFrameFrom, int animationFrameTo, float factor, int level,
-			int dx, int dy, int w, int h) {
+			int dx, int dy, int w, int h, int mapWidth, int mapHeight) {
 		if(cacheTextures == null) {
 			throw new IllegalStateException("Cannot copy from uninitialized TextureAnimationCache");
 		}
 		
-		GlStateManager.viewport(dx, dy, w, h);
 		GlStateManager.bindTexture(cacheTextures[level]);
 		GlStateManager.disableBlend();
 		
-		Matrix3f matrix = new Matrix3f();
-		matrix.m11 = 1.0f / frameCount;
-		matrix.m21 = matrix.m11 * animationFrameFrom;
-		
-		SpriteLevelMixer.setMatrix3f(matrix);
+		SpriteLevelMixer.srcSize(width >> level, (height >> level) * frameCount);
+		SpriteLevelMixer.dstSize(mapWidth, mapHeight);
 		SpriteLevelMixer.setBlendColor(factor, factor, factor, factor);
 		SpriteLevelMixer.setBiasColor(0.0f, 0.0f, 0.0f, 0.0f);
 		
-		SpriteLevelMixer.drawSprite(0);
+		SpriteLevelMixer.drawSprite(0, 0, h * animationFrameFrom, w, h, dx, dy, w, h);
 		
-		matrix.m21 = matrix.m11 * animationFrameTo;
-		SpriteLevelMixer.setMatrix3f(matrix);
 		float fac1 = 1.0f - factor;
 		SpriteLevelMixer.setBlendColor(fac1, fac1, fac1, fac1);
 		
 		GlStateManager.enableBlend();
 		GlStateManager.blendFunc(GL_ONE, GL_ONE);
 		
-		SpriteLevelMixer.drawSprite(0);
+		SpriteLevelMixer.drawSprite(0, 0, h * animationFrameTo, w, h, dx, dy, w, h);
 		
 		GlStateManager.disableBlend();
 		GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java
index 41866dae..a32b3b37 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java
@@ -246,6 +246,7 @@ public class EaglercraftGPU {
 
 	public static final void glTexImage2D(int target, int level, int internalFormat, int w, int h, int unused,
 			int format, int type, ByteBuffer pixels) {
+		GlStateManager.setTextureCachedSize(target, w, h);
 		if(glesVers >= 300) {
 			_wglTexImage2D(target, level, internalFormat, w, h, unused, format, type, pixels);
 		}else {
@@ -256,6 +257,7 @@ public class EaglercraftGPU {
 
 	public static final void glTexImage2D(int target, int level, int internalFormat, int w, int h, int unused,
 			int format, int type, IntBuffer pixels) {
+		GlStateManager.setTextureCachedSize(target, w, h);
 		if(glesVers >= 300) {
 			_wglTexImage2D(target, level, internalFormat, w, h, unused, format, type, pixels);
 		}else {
@@ -270,6 +272,7 @@ public class EaglercraftGPU {
 	}
 
 	public static final void glTexStorage2D(int target, int levels, int internalFormat, int w, int h) {
+		GlStateManager.setTextureCachedSize(target, w, h);
 		if(texStorageCapable && (glesVers >= 300 || levels == 1 || (MathHelper.calculateLogBaseTwo(Math.max(w, h)) + 1) == levels)) {
 			_wglTexStorage2D(target, levels, internalFormat, w, h);
 		}else {
@@ -914,7 +917,6 @@ public class EaglercraftGPU {
 		PlatformOpenGL.enterVAOEmulationHook();
 		GLSLHeader.init();
 		DrawUtils.init();
-		SpriteLevelMixer.initialize();
 		if(instancingCapable) {
 			InstancedFontRenderer.initialize();
 			InstancedParticleRenderer.initialize();
@@ -928,7 +930,6 @@ public class EaglercraftGPU {
 	public static final void destroyCache() {
 		GLSLHeader.destroy();
 		DrawUtils.destroy();
-		SpriteLevelMixer.destroy();
 		InstancedFontRenderer.destroy();
 		InstancedParticleRenderer.destroy();
 		EffectPipelineFXAA.destroy();
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java
index 537dfb50..1041c98b 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java
@@ -1,11 +1,13 @@
 package net.lax1dude.eaglercraft.v1_8.opengl;
 
+import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL;
 import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
 import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
 import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
 import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f;
 import net.lax1dude.eaglercraft.v1_8.vector.Vector3f;
 import net.lax1dude.eaglercraft.v1_8.vector.Vector4f;
+import net.minecraft.util.MathHelper;
 
 import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
 import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
@@ -159,8 +161,8 @@ public class GlStateManager {
 	
 	static final Matrix4f[][] textureMatrixStack = new Matrix4f[8][8];
 	static final int[][] textureMatrixStackAccessSerial = new int[8][8];
-	static int[] textureMatrixAccessSerial = new int[8];
-	static int[] textureMatrixStackPointer = new int[8];
+	static final int[] textureMatrixAccessSerial = new int[8];
+	static final int[] textureMatrixStackPointer = new int[8];
 	
 	static boolean stateUseExtensionPipeline = false;
 	
@@ -821,6 +823,32 @@ public class GlStateManager {
 		}
 	}
 
+	private static Matrix4f getMatrixIncr() {
+		Matrix4f mat;
+		int _i, _j;
+		switch(stateMatrixMode) {
+		case GL_MODELVIEW:
+			_j = modelMatrixStackPointer;
+			mat = modelMatrixStack[_j];
+			modelMatrixStackAccessSerial[_j] = ++modelMatrixAccessSerial;
+			break;
+		case GL_PROJECTION:
+			_j = projectionMatrixStackPointer;
+			mat = projectionMatrixStack[_j];
+			projectionMatrixStackAccessSerial[_j] = ++projectionMatrixAccessSerial;
+			break;
+		case GL_TEXTURE:
+			_i = activeTexture;
+			_j = textureMatrixStackPointer[_i];
+			mat = textureMatrixStack[_i][_j];
+			textureMatrixStackAccessSerial[_i][_j] = ++textureCoordsAccessSerial[_i];
+			break;
+		default:
+			throw new IllegalStateException();
+		}
+		return mat;
+	}
+
 	public static final void getFloat(int pname, float[] params) {
 		switch(pname) {
 		case GL_MODELVIEW_MATRIX:
@@ -854,24 +882,7 @@ public class GlStateManager {
 	}
 
 	public static final void ortho(double left, double right, double bottom, double top, double zNear, double zFar) {
-		Matrix4f matrix;
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-			matrix = modelMatrixStack[modelMatrixStackPointer];
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-		default:
-			matrix = projectionMatrixStack[projectionMatrixStackPointer];
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			matrix = textureMatrixStack[activeTexture][ptr];
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
+		Matrix4f matrix = getMatrixIncr();
 		paramMatrix.m00 = 2.0f / (float)(right - left);
 		paramMatrix.m01 = 0.0f;
 		paramMatrix.m02 = 0.0f;
@@ -894,169 +905,200 @@ public class GlStateManager {
 	private static final Vector3f paramVector = new Vector3f();
 	private static final float toRad = 0.0174532925f;
 	public static final void rotate(float angle, float x, float y, float z) {
-		paramVector.x = x;
-		paramVector.y = y;
-		paramVector.z = z;
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-		default:
-			modelMatrixStack[modelMatrixStackPointer].rotate(angle * toRad, paramVector);
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-			projectionMatrixStack[projectionMatrixStackPointer].rotate(angle * toRad, paramVector);
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			textureMatrixStack[activeTexture][ptr].rotate(angle * toRad, paramVector);
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
+		Matrix4f matrix = getMatrixIncr();
+		if(x == 0.0f) {
+			if(y == 0.0f) {
+				if(z == 1.0f || z == -1.0f) {
+					_glRotatefZ(matrix, toRad * angle * z);
+					return;
+				}
+			}else if((y == 1.0f || y == -1.0f) && z == 0.0f) {
+				_glRotatefY(matrix, toRad * angle * y);
+				return;
+			}
+		}else if((x == 1.0f || x == -1.0f) && y == 0.0f && z == 0.0f) {
+			_glRotatefX(matrix, toRad * angle * x);
+			return;
 		}
+		_glRotatef(matrix, toRad * angle, x, y, z);
+	}
+
+	public static final void rotateXYZ(float x, float y, float z) {
+		Matrix4f matrix = getMatrixIncr();
+		if(x != 0.0f) _glRotatefX(matrix, toRad * x);
+		if(y != 0.0f) _glRotatefY(matrix, toRad * y);
+		if(z != 0.0f) _glRotatefZ(matrix, toRad * z);
+	}
+
+	public static final void rotateZYX(float x, float y, float z) {
+		Matrix4f matrix = getMatrixIncr();
+		if(z != 0.0f) _glRotatefZ(matrix, toRad * z);
+		if(y != 0.0f) _glRotatefY(matrix, toRad * y);
+		if(x != 0.0f) _glRotatefX(matrix, toRad * x);
+	}
+
+	public static final void rotateXYZRad(float x, float y, float z) {
+		Matrix4f matrix = getMatrixIncr();
+		if(x != 0.0f) _glRotatefX(matrix, x);
+		if(y != 0.0f) _glRotatefY(matrix, y);
+		if(z != 0.0f) _glRotatefZ(matrix, z);
+	}
+
+	public static final void rotateZYXRad(float x, float y, float z) {
+		Matrix4f matrix = getMatrixIncr();
+		if(z != 0.0f) _glRotatefZ(matrix, z);
+		if(y != 0.0f) _glRotatefY(matrix, y);
+		if(x != 0.0f) _glRotatefX(matrix, x);
+	}
+
+	private static void _glRotatefX(Matrix4f mat, float angle) {
+		float sin = MathHelper.sin(angle);
+		float cos = MathHelper.cos(angle);
+		float lm10 = mat.m10, lm11 = mat.m11, lm12 = mat.m12, lm13 = mat.m13, lm20 = mat.m20, lm21 = mat.m21,
+				lm22 = mat.m22, lm23 = mat.m23;
+		mat.m20 = lm10 * -sin + lm20 * cos;
+		mat.m21 = lm11 * -sin + lm21 * cos;
+		mat.m22 = lm12 * -sin + lm22 * cos;
+		mat.m23 = lm13 * -sin + lm23 * cos;
+		mat.m10 = lm10 * cos + lm20 * sin;
+		mat.m11 = lm11 * cos + lm21 * sin;
+		mat.m12 = lm12 * cos + lm22 * sin;
+		mat.m13 = lm13 * cos + lm23 * sin;
+	}
+
+	private static void _glRotatefY(Matrix4f mat, float angle) {
+		float sin = MathHelper.sin(angle);
+		float cos = MathHelper.cos(angle);
+		float nm00 = mat.m00 * cos + mat.m20 * -sin;
+		float nm01 = mat.m01 * cos + mat.m21 * -sin;
+		float nm02 = mat.m02 * cos + mat.m22 * -sin;
+		float nm03 = mat.m03 * cos + mat.m23 * -sin;
+		mat.m20 = mat.m00 * sin + mat.m20 * cos;
+		mat.m21 = mat.m01 * sin + mat.m21 * cos;
+		mat.m22 = mat.m02 * sin + mat.m22 * cos;
+		mat.m23 = mat.m03 * sin + mat.m23 * cos;
+		mat.m00 = nm00;
+		mat.m01 = nm01;
+		mat.m02 = nm02;
+		mat.m03 = nm03;
+	}
+
+	private static void _glRotatefZ(Matrix4f mat, float angle) {
+		float dirX = MathHelper.sin(angle);
+		float dirY = MathHelper.cos(angle);
+		float nm00 = mat.m00 * dirY + mat.m10 * dirX;
+		float nm01 = mat.m01 * dirY + mat.m11 * dirX;
+		float nm02 = mat.m02 * dirY + mat.m12 * dirX;
+		float nm03 = mat.m03 * dirY + mat.m13 * dirX;
+		mat.m10 = mat.m00 * -dirX + mat.m10 * dirY;
+		mat.m11 = mat.m01 * -dirX + mat.m11 * dirY;
+		mat.m12 = mat.m02 * -dirX + mat.m12 * dirY;
+		mat.m13 = mat.m03 * -dirX + mat.m13 * dirY;
+		mat.m00 = nm00;
+		mat.m01 = nm01;
+		mat.m02 = nm02;
+		mat.m03 = nm03;
+	}
+
+	private static void _glRotatef(Matrix4f mat, float angle, float x, float y, float z) {
+		float s = MathHelper.sin(angle);
+		float c = MathHelper.cos(angle);
+		float C = 1.0f - c;
+		float xx = x * x, xy = x * y, xz = x * z;
+		float yy = y * y, yz = y * z;
+		float zz = z * z;
+		float rm00 = xx * C + c;
+		float rm01 = xy * C + z * s;
+		float rm02 = xz * C - y * s;
+		float rm10 = xy * C - z * s;
+		float rm11 = yy * C + c;
+		float rm12 = yz * C + x * s;
+		float rm20 = xz * C + y * s;
+		float rm21 = yz * C - x * s;
+		float rm22 = zz * C + c;
+		float nm00 = mat.m00 * rm00 + mat.m10 * rm01 + mat.m20 * rm02;
+		float nm01 = mat.m01 * rm00 + mat.m11 * rm01 + mat.m21 * rm02;
+		float nm02 = mat.m02 * rm00 + mat.m12 * rm01 + mat.m22 * rm02;
+		float nm03 = mat.m03 * rm00 + mat.m13 * rm01 + mat.m23 * rm02;
+		float nm10 = mat.m00 * rm10 + mat.m10 * rm11 + mat.m20 * rm12;
+		float nm11 = mat.m01 * rm10 + mat.m11 * rm11 + mat.m21 * rm12;
+		float nm12 = mat.m02 * rm10 + mat.m12 * rm11 + mat.m22 * rm12;
+		float nm13 = mat.m03 * rm10 + mat.m13 * rm11 + mat.m23 * rm12;
+		mat.m20 = mat.m00 * rm20 + mat.m10 * rm21 + mat.m20 * rm22;
+		mat.m21 = mat.m01 * rm20 + mat.m11 * rm21 + mat.m21 * rm22;
+		mat.m22 = mat.m02 * rm20 + mat.m12 * rm21 + mat.m22 * rm22;
+		mat.m23 = mat.m03 * rm20 + mat.m13 * rm21 + mat.m23 * rm22;
+		mat.m00 = nm00;
+		mat.m01 = nm01;
+		mat.m02 = nm02;
+		mat.m03 = nm03;
+		mat.m10 = nm10;
+		mat.m11 = nm11;
+		mat.m12 = nm12;
+		mat.m13 = nm13;
 	}
 
 	public static final void scale(float x, float y, float z) {
-		paramVector.x = x;
-		paramVector.y = y;
-		paramVector.z = z;
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-		default:
-			modelMatrixStack[modelMatrixStackPointer].scale(paramVector);
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-			projectionMatrixStack[projectionMatrixStackPointer].scale(paramVector);
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			textureMatrixStack[activeTexture][ptr].scale(paramVector);
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
+		Matrix4f matrix = getMatrixIncr();
+		matrix.m00 *= x;
+		matrix.m01 *= x;
+		matrix.m02 *= x;
+		matrix.m03 *= x;
+		matrix.m10 *= y;
+		matrix.m11 *= y;
+		matrix.m12 *= y;
+		matrix.m13 *= y;
+		matrix.m20 *= z;
+		matrix.m21 *= z;
+		matrix.m22 *= z;
+		matrix.m23 *= z;
 	}
 
 	public static final void scale(double x, double y, double z) {
-		paramVector.x = (float)x;
-		paramVector.y = (float)y;
-		paramVector.z = (float)z;
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-		default:
-			modelMatrixStack[modelMatrixStackPointer].scale(paramVector);
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-			projectionMatrixStack[projectionMatrixStackPointer].scale(paramVector);
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			textureMatrixStack[activeTexture][ptr].scale(paramVector);
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
+		Matrix4f matrix = getMatrixIncr();
+		matrix.m00 *= x;
+		matrix.m01 *= x;
+		matrix.m02 *= x;
+		matrix.m03 *= x;
+		matrix.m10 *= y;
+		matrix.m11 *= y;
+		matrix.m12 *= y;
+		matrix.m13 *= y;
+		matrix.m20 *= z;
+		matrix.m21 *= z;
+		matrix.m22 *= z;
+		matrix.m23 *= z;
 	}
 
 	public static final void translate(float x, float y, float z) {
-		paramVector.x = x;
-		paramVector.y = y;
-		paramVector.z = z;
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-		default:
-			modelMatrixStack[modelMatrixStackPointer].translate(paramVector);
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-			projectionMatrixStack[projectionMatrixStackPointer].translate(paramVector);
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			textureMatrixStack[activeTexture][ptr].translate(paramVector);
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
+		Matrix4f matrix = getMatrixIncr();
+		matrix.m30 = matrix.m00 * x + matrix.m10 * y + matrix.m20 * z + matrix.m30;
+		matrix.m31 = matrix.m01 * x + matrix.m11 * y + matrix.m21 * z + matrix.m31;
+		matrix.m32 = matrix.m02 * x + matrix.m12 * y + matrix.m22 * z + matrix.m32;
+		matrix.m33 = matrix.m03 * x + matrix.m13 * y + matrix.m23 * z + matrix.m33;
 	}
 
 	public static final void translate(double x, double y, double z) {
-		paramVector.x = (float)x;
-		paramVector.y = (float)y;
-		paramVector.z = (float)z;
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-		default:
-			modelMatrixStack[modelMatrixStackPointer].translate(paramVector);
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-			projectionMatrixStack[projectionMatrixStackPointer].translate(paramVector);
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			textureMatrixStack[activeTexture][ptr].translate(paramVector);
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
+		float _x = (float)x;
+		float _y = (float)y;
+		float _z = (float)z;
+		Matrix4f matrix = getMatrixIncr();
+		matrix.m30 = matrix.m00 * _x + matrix.m10 * _y + matrix.m20 * _z + matrix.m30;
+		matrix.m31 = matrix.m01 * _x + matrix.m11 * _y + matrix.m21 * _z + matrix.m31;
+		matrix.m32 = matrix.m02 * _x + matrix.m12 * _y + matrix.m22 * _z + matrix.m32;
+		matrix.m33 = matrix.m03 * _x + matrix.m13 * _y + matrix.m23 * _z + matrix.m33;
 	}
 
 	private static final Matrix4f paramMatrix = new Matrix4f();
 	public static final void multMatrix(float[] matrix) {
-		Matrix4f modeMatrix;
-		
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-		default:
-			modeMatrix = modelMatrixStack[modelMatrixStackPointer];
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-			modeMatrix = projectionMatrixStack[projectionMatrixStackPointer];
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			modeMatrix = textureMatrixStack[activeTexture][ptr];
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
-		
 		paramMatrix.load(matrix);
-		
-		Matrix4f.mul(modeMatrix, paramMatrix, modeMatrix);
+		Matrix4f mat = getMatrixIncr();
+		Matrix4f.mul(mat, paramMatrix, mat);
 	}
 
 	public static final void multMatrix(Matrix4f matrix) {
-		Matrix4f modeMatrix;
-		
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-		default:
-			modeMatrix = modelMatrixStack[modelMatrixStackPointer];
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-			modeMatrix = projectionMatrixStack[projectionMatrixStackPointer];
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			modeMatrix = textureMatrixStack[activeTexture][ptr];
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
-		
-		Matrix4f.mul(modeMatrix, matrix, modeMatrix);
+		Matrix4f mat = getMatrixIncr();
+		Matrix4f.mul(mat, matrix, mat);
 	}
 
 	public static final void color(float colorRed, float colorGreen, float colorBlue, float colorAlpha) {
@@ -1088,24 +1130,7 @@ public class GlStateManager {
 	}
 
 	public static final void gluPerspective(float fovy, float aspect, float zNear, float zFar) {
-		Matrix4f matrix;
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-			matrix = modelMatrixStack[modelMatrixStackPointer];
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-		default:
-			matrix = projectionMatrixStack[projectionMatrixStackPointer];
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			matrix = textureMatrixStack[activeTexture][ptr];
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
+		Matrix4f matrix = getMatrixIncr();
 		float cotangent = (float) Math.cos(fovy * toRad * 0.5f) / (float) Math.sin(fovy * toRad * 0.5f);
 		paramMatrix.m00 = cotangent / aspect;
 		paramMatrix.m01 = 0.0f;
@@ -1127,24 +1152,7 @@ public class GlStateManager {
 	}
 
 	public static final void gluLookAt(Vector3f eye, Vector3f center, Vector3f up) {
-		Matrix4f matrix;
-		switch(stateMatrixMode) {
-		case GL_MODELVIEW:
-			matrix = modelMatrixStack[modelMatrixStackPointer];
-			modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial;
-			break;
-		case GL_PROJECTION:
-		default:
-			matrix = projectionMatrixStack[projectionMatrixStackPointer];
-			projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial;
-			break;
-		case GL_TEXTURE:
-			int ptr = textureMatrixStackPointer[activeTexture];
-			matrix = textureMatrixStack[activeTexture][ptr];
-			textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] =
-					++textureMatrixAccessSerial[activeTexture];
-			break;
-		}
+		Matrix4f matrix = getMatrixIncr();
 		float x = center.x - eye.x;
 		float y = center.y - eye.y;
 		float z = center.z - eye.z;
@@ -1262,4 +1270,18 @@ public class GlStateManager {
 	public static void recompileShaders() {
 		FixedFunctionPipeline.flushCache();
 	}
+
+	public static int getBoundTexture() {
+		return boundTexture[activeTexture];
+	}
+
+	static void setTextureCachedSize(int target, int w, int h) {
+		if(target == GL_TEXTURE_2D) {
+			ITextureGL tex = EaglercraftGPU.getNativeTexture(boundTexture[activeTexture]);
+			if(tex != null) {
+				tex.setCacheSize(w, h);
+			}
+		}
+	}
+
 }
\ No newline at end of file
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java
index e01dbd39..3d454556 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java
@@ -1,6 +1,9 @@
 package net.lax1dude.eaglercraft.v1_8.opengl;
 
 import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
+
+import java.util.List;
+
 import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
 
 import net.lax1dude.eaglercraft.v1_8.EagRuntime;
@@ -10,10 +13,11 @@ import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL;
 import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
 import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
 import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
+import net.lax1dude.eaglercraft.v1_8.opengl.VSHInputLayoutParser.ShaderInput;
 import net.lax1dude.eaglercraft.v1_8.vector.Matrix3f;
 
 /**
- * Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
+ * Copyright (c) 2022-2025 lax1dude. All Rights Reserved.
  * 
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -39,7 +43,8 @@ public class SpriteLevelMixer {
 	private static IUniformGL u_textureLod1f = null;
 	private static IUniformGL u_blendFactor4f = null;
 	private static IUniformGL u_blendBias4f = null;
-	private static IUniformGL u_matrixTransform = null;
+	private static IUniformGL u_srcCoords4f = null;
+	private static IUniformGL u_dstCoords4f = null;
 
 	private static FloatBuffer matrixCopyBuffer = null;
 
@@ -55,12 +60,15 @@ public class SpriteLevelMixer {
 	private static float biasColorB = 0.0f;
 	private static float biasColorA = 0.0f;
 
-	private static boolean matrixChanged = true;
-	private static final Matrix3f transformMatrix = new Matrix3f();
+	private static float srcViewW = 100.0f;
+	private static float srcViewH = 100.0f;
+
+	private static float dstViewW = 50.0f;
+	private static float dstViewH = 50.0f;
 
 	private static final Matrix3f identityMatrix = new Matrix3f();
 
-	static void initialize() {
+	static void initialize(IShaderGL vertexShader, List<ShaderInput> vshSourceLayout) {
 		String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath);
 
 		IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER);
@@ -82,16 +90,16 @@ public class SpriteLevelMixer {
 
 		shaderProgram = _wglCreateProgram();
 
-		_wglAttachShader(shaderProgram, DrawUtils.vshLocal);
+		_wglAttachShader(shaderProgram, vertexShader);
 		_wglAttachShader(shaderProgram, frag);
 
 		if(EaglercraftGPU.checkOpenGLESVersion() == 200) {
-			VSHInputLayoutParser.applyLayout(shaderProgram, DrawUtils.vshLocalLayout);
+			VSHInputLayoutParser.applyLayout(shaderProgram, vshSourceLayout);
 		}
 
 		_wglLinkProgram(shaderProgram);
 
-		_wglDetachShader(shaderProgram, DrawUtils.vshLocal);
+		_wglDetachShader(shaderProgram, vertexShader);
 		_wglDetachShader(shaderProgram, frag);
 
 		_wglDeleteShader(frag);
@@ -115,7 +123,8 @@ public class SpriteLevelMixer {
 		u_textureLod1f = _wglGetUniformLocation(shaderProgram, "u_textureLod1f");
 		u_blendFactor4f = _wglGetUniformLocation(shaderProgram, "u_blendFactor4f");
 		u_blendBias4f = _wglGetUniformLocation(shaderProgram, "u_blendBias4f");
-		u_matrixTransform = _wglGetUniformLocation(shaderProgram, "u_matrixTransform");
+		u_srcCoords4f = _wglGetUniformLocation(shaderProgram, "u_srcCoords4f");
+		u_dstCoords4f = _wglGetUniformLocation(shaderProgram, "u_dstCoords4f");
 
 		_wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0);
 
@@ -141,25 +150,31 @@ public class SpriteLevelMixer {
 		}
 	}
 
-	public static void setIdentityMatrix() {
-		setMatrix3f(identityMatrix);
+	public static void srcSize(int w, int h) {
+		srcViewW = w;
+		srcViewH = h;
 	}
 
-	public static void setMatrix3f(Matrix3f matrix) {
-		if(!matrix.equals(transformMatrix)) {
-			matrixChanged = true;
-			transformMatrix.load(matrix);
-		}
+	public static void dstSize(int w, int h) {
+		dstViewW = w * 0.5f;
+		dstViewH = h * 0.5f;
 	}
 
-	public static void drawSprite(float level) {
+	public static void srcDstSize(int w, int h) {
+		srcViewW = w;
+		srcViewH = h;
+		dstViewW = w * 0.5f;
+		dstViewH = h * 0.5f;
+	}
+
+	public static void drawSprite(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) {
 		EaglercraftGPU.bindGLShaderProgram(shaderProgram);
 		
 		if(EaglercraftGPU.checkTextureLODCapable()) {
-			_wglUniform1f(u_textureLod1f, level);
+			_wglUniform1f(u_textureLod1f, lvl);
 		}else {
-			if(level != 0.0f) {
-				LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", level);
+			if(lvl != 0) {
+				LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", lvl);
 			}
 			_wglUniform1f(u_textureLod1f, 0.0f);
 		}
@@ -174,13 +189,9 @@ public class SpriteLevelMixer {
 			biasColorChanged = false;
 		}
 		
-		if(matrixChanged) {
-			matrixCopyBuffer.clear();
-			transformMatrix.store(matrixCopyBuffer);
-			matrixCopyBuffer.flip();
-			_wglUniformMatrix3fv(u_matrixTransform, false, matrixCopyBuffer);
-			matrixChanged = false;
-		}
+		_wglUniform4f(u_srcCoords4f, (float)srcX / srcViewW, (float)srcY / srcViewH, (float)srcW / srcViewW, (float)srcH / srcViewH);
+		_wglUniform4f(u_dstCoords4f, (float) dstX / dstViewW - 1.0f, (float) dstY / dstViewH - 1.0f,
+				(float) dstW / dstViewW, (float) dstH / dstViewH);
 		
 		DrawUtils.drawStandardQuad2D();
 	}
@@ -197,7 +208,8 @@ public class SpriteLevelMixer {
 		u_textureLod1f = null;
 		u_blendFactor4f = null;
 		u_blendBias4f = null;
-		u_matrixTransform = null;
+		u_srcCoords4f = null;
+		u_dstCoords4f = null;
 	}
 
 }
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java
index b5f6f9f4..1416e5dd 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java
@@ -113,6 +113,8 @@ public class TextureCopyUtil {
 		if(EaglercraftGPU.checkOpenGLESVersion() == 200) {
 			vshSourceLayout = VSHInputLayoutParser.getShaderInputs(vshSource);
 		}
+
+		SpriteLevelMixer.initialize(vshShader, vshSourceLayout);
 	}
 
 	private static TextureCopyShader compileShader(boolean align, boolean depth) {
@@ -283,18 +285,22 @@ public class TextureCopyUtil {
 		DrawUtils.drawStandardQuad2D();
 	}
 
+	@Deprecated
 	public static void blitTextureUsingViewports(int srcX, int srcY, int dstX, int dstY, int w, int h) {
 		blitTextureUsingViewports(0, srcX, srcY, w, h, dstX, dstY, w, h);
 	}
 
+	@Deprecated
 	public static void blitTextureUsingViewports(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) {
 		blitTextureUsingViewports(lvl, srcX, srcY, w, h, dstX, dstY, w, h);
 	}
 
+	@Deprecated
 	public static void blitTextureUsingViewports(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) {
 		blitTextureUsingViewports(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH);
 	}
 
+	@Deprecated
 	public static void blitTextureUsingViewports(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) {
 		TextureCopyShader shaderObj = getShaderObj(isAligned, false);
 		EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram);
@@ -376,18 +382,22 @@ public class TextureCopyUtil {
 		DrawUtils.drawStandardQuad2D();
 	}
 
+	@Deprecated
 	public static void blitTextureDepthUsingViewports(int srcX, int srcY, int dstX, int dstY, int w, int h) {
 		blitTextureDepthUsingViewports(0, srcX, srcY, w, h, dstX, dstY, w, h);
 	}
 
+	@Deprecated
 	public static void blitTextureDepthUsingViewports(int lvl, int srcX, int srcY, int dstX, int dstY, int w, int h) {
 		blitTextureDepthUsingViewports(lvl, srcX, srcY, w, h, dstX, dstY, w, h);
 	}
 
+	@Deprecated
 	public static void blitTextureDepthUsingViewports(int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) {
 		blitTextureDepthUsingViewports(0, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH);
 	}
 
+	@Deprecated
 	public static void blitTextureDepthUsingViewports(int lvl, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH) {
 		TextureCopyShader shaderObj = getShaderObj(isAligned, true);
 		EaglercraftGPU.bindGLShaderProgram(shaderObj.shaderProgram);
@@ -431,5 +441,6 @@ public class TextureCopyUtil {
 			textureBlitDepthAligned.destroy();
 			textureBlitDepthAligned = null;
 		}
+		SpriteLevelMixer.destroy();
 	}
 }
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java
index c5397b4c..42372b3d 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java
@@ -12,8 +12,12 @@ import net.lax1dude.eaglercraft.v1_8.EagRuntime;
 import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
 import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
 import net.lax1dude.eaglercraft.v1_8.vector.Vector3f;
+import net.minecraft.block.state.IBlockState;
 import net.minecraft.client.renderer.GLAllocation;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumWorldBlockLayer;
 import net.minecraft.util.MathHelper;
+import net.optifine.render.RenderEnv;
 
 /**
  * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
@@ -50,6 +54,9 @@ public class WorldRenderer {
 	
 	private boolean hasBeenFreed = false;
 
+	private EnumWorldBlockLayer blockLayer = null;
+	public RenderEnv renderEnv = null;
+
 	public WorldRenderer(int bufferSizeIn) {
 		this.byteBuffer = GLAllocation.createDirectByteBuffer(bufferSizeIn << 2);
 		this.intBuffer = this.byteBuffer.asIntBuffer();
@@ -555,6 +562,20 @@ public class WorldRenderer {
 
 	}
 
+	public RenderEnv getRenderEnv(IBlockState p_getRenderEnv_1_, BlockPos p_getRenderEnv_2_) {
+		if (this.renderEnv == null) {
+			this.renderEnv = new RenderEnv(p_getRenderEnv_1_, p_getRenderEnv_2_);
+			return this.renderEnv;
+		} else {
+			this.renderEnv.reset(p_getRenderEnv_1_, p_getRenderEnv_2_);
+			return this.renderEnv;
+		}
+	}
+
+	public EnumWorldBlockLayer getBlockLayer() {
+		return this.blockLayer;
+	}
+
 	public class State {
 		private final IntBuffer stateRawBuffer;
 		private final VertexFormat stateVertexFormat;
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java
index d5ebccf1..8886ed8a 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java
@@ -2440,6 +2440,15 @@ public class EaglerDeferredPipeline {
 		GlStateManager.setActiveTexture(GL_TEXTURE10);
 		GlStateManager.bindTexture(skyIrradianceTexture);
 		GlStateManager.setActiveTexture(GL_TEXTURE0);
+		GlStateManager.disableDepth();
+		GlStateManager.disableBlend();
+		GlStateManager.depthMask(false);
+		GlStateManager.bindTexture(envMapSkyTexture);
+		GlStateManager.viewport(0, 0, 128, 256);
+		TextureCopyUtil.blitTexture();
+		GlStateManager.depthMask(true);
+		GlStateManager.enableBlend();
+		GlStateManager.enableDepth();
 		DeferredStateManager.checkGLError("Post: beginDrawEnvMap()");
 	}
 
@@ -2470,7 +2479,7 @@ public class EaglerDeferredPipeline {
 	public void beginDrawEnvMapTranslucent() {
 		DeferredStateManager.checkGLError("Pre: beginDrawEnvMapTranslucent()");
 		GlStateManager.enableBlend();
-		GlStateManager.tryBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
+		GlStateManager.tryBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
 		bindEnvMapBlockTexture();
 		DeferredStateManager.checkGLError("Post: beginDrawEnvMapTranslucent()");
 	}
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java
index 05d31bdf..aa4f946b 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java
@@ -8,7 +8,6 @@ import com.carrotsearch.hppc.cursors.IntCursor;
 import com.google.common.collect.Lists;
 
 import net.lax1dude.eaglercraft.v1_8.HString;
-import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL;
 import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
 import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
 import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
@@ -219,7 +218,9 @@ public class EaglerTextureAtlasSpritePBR extends EaglerTextureAtlasSprite {
 		}
 	}
 
-	public void updateAnimationPBR(IFramebufferGL[] copyColorFramebuffer, IFramebufferGL[] copyMaterialFramebuffer, int materialTexOffset) {
+	protected IAnimCopyFunction currentAnimUpdaterPBR = null;
+
+	public void updateAnimationPBR() {
 		if(animationCachePBR[0] == null || (!dontAnimateNormals && animationCachePBR[1] == null)
 				|| (!dontAnimateMaterial && animationCachePBR[2] == null)) {
 			throw new IllegalStateException("Animation cache for '" + this.iconName + "' was never baked!");
@@ -233,9 +234,28 @@ public class EaglerTextureAtlasSpritePBR extends EaglerTextureAtlasSprite {
 			this.tickCounter = 0;
 			int k = this.animationMetadata.getFrameIndex(this.frameCounter);
 			if (i != k && k >= 0 && k < this.frameTextureDataPBR[0].size()) {
-				animationCachePBR[0].copyFrameLevelsToTex2D(k, this.originX, this.originY, this.width, this.height, copyColorFramebuffer);
-				if(!dontAnimateNormals) animationCachePBR[1].copyFrameLevelsToTex2D(k, this.originX, this.originY, this.width, this.height, copyMaterialFramebuffer);
-				if(!dontAnimateMaterial) animationCachePBR[2].copyFrameLevelsToTex2D(k, this.originX, this.originY + materialTexOffset, this.width, this.height, copyMaterialFramebuffer);
+				currentAnimUpdater = (mapWidth, mapHeight, mapLevel) -> {
+					animationCachePBR[0].copyFrameToTex2D(k, mapLevel, this.originX >> mapLevel,
+							this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+							mapHeight);
+				};
+				if(!dontAnimateNormals || !dontAnimateMaterial) {
+					currentAnimUpdaterPBR = (mapWidth, mapHeight, mapLevel) -> {
+						if (!dontAnimateNormals)
+							animationCachePBR[1].copyFrameToTex2D(k, mapLevel, this.originX >> mapLevel,
+									this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+									mapHeight);
+						if (!dontAnimateMaterial)
+							animationCachePBR[2].copyFrameToTex2D(k, mapLevel, this.originX >> mapLevel,
+									(this.originY >> mapLevel) + (mapHeight >> 1), this.width >> mapLevel,
+									this.height >> mapLevel, mapWidth, mapHeight);
+					};
+				}else {
+					currentAnimUpdaterPBR = null;
+				}
+			}else {
+				currentAnimUpdater = null;
+				currentAnimUpdaterPBR = null;
 			}
 		} else if (this.animationMetadata.isInterpolate()) {
 			float f = 1.0f - (float) this.tickCounter / (float) this.animationMetadata.getFrameTimeSingle(this.frameCounter);
@@ -244,9 +264,43 @@ public class EaglerTextureAtlasSpritePBR extends EaglerTextureAtlasSprite {
 					: this.animationMetadata.getFrameCount();
 			int k = this.animationMetadata.getFrameIndex((this.frameCounter + 1) % j);
 			if (i != k && k >= 0 && k < this.frameTextureDataPBR[0].size()) {
-				animationCachePBR[0].copyInterpolatedFrameLevelsToTex2D(i, k, f, this.originX, this.originY, this.width, this.height, copyColorFramebuffer);
-				if(!dontAnimateNormals) animationCachePBR[1].copyInterpolatedFrameLevelsToTex2D(i, k, f, this.originX, this.originY, this.width, this.height, copyMaterialFramebuffer);
-				if(!dontAnimateMaterial) animationCachePBR[2].copyInterpolatedFrameLevelsToTex2D(i, k, f, this.originX, this.originY + materialTexOffset, this.width, this.height, copyMaterialFramebuffer);
+				currentAnimUpdater = (mapWidth, mapHeight, mapLevel) -> {
+					animationCachePBR[0].copyInterpolatedFrameToTex2D(i, k, f, mapLevel, this.originX >> mapLevel,
+							this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+							mapHeight);
+				};
+				if(!dontAnimateNormals || !dontAnimateMaterial) {
+					currentAnimUpdaterPBR = (mapWidth, mapHeight, mapLevel) -> {
+						if (!dontAnimateNormals)
+							animationCachePBR[1].copyInterpolatedFrameToTex2D(i, k, f, mapLevel,
+									this.originX >> mapLevel, this.originY >> mapLevel, this.width >> mapLevel,
+									this.height >> mapLevel, mapWidth, mapHeight);
+						if (!dontAnimateMaterial)
+							animationCachePBR[2].copyInterpolatedFrameToTex2D(i, k, f, mapLevel,
+									this.originX >> mapLevel, (this.originY >> mapLevel) + (mapHeight >> 1),
+									this.width >> mapLevel, this.height >> mapLevel, mapWidth, mapHeight);
+					};
+				}else {
+					currentAnimUpdaterPBR = null;
+				}
+			}else {
+				currentAnimUpdater = null;
+				currentAnimUpdaterPBR = null;
+			}
+		}else {
+			currentAnimUpdater = null;
+			currentAnimUpdaterPBR = null;
+		}
+	}
+
+	public void copyAnimationFramePBR(int pass, int mapWidth, int mapHeight, int mapLevel) {
+		if(pass == 0) {
+			if(currentAnimUpdater != null) {
+				currentAnimUpdater.updateAnimation(mapWidth, mapHeight, mapLevel);
+			}
+		}else {
+			if(currentAnimUpdaterPBR != null) {
+				currentAnimUpdaterPBR.updateAnimation(mapWidth, mapHeight, mapLevel);
 			}
 		}
 	}
@@ -279,7 +333,7 @@ public class EaglerTextureAtlasSpritePBR extends EaglerTextureAtlasSprite {
 		}
 	}
 
-	public void updateAnimation(IFramebufferGL[] fb) {
+	public void updateAnimation() {
 		Throwable t = new UnsupportedOperationException("Cannot call regular updateAnimation in PBR mode, use updateAnimationPBR");
 		try {
 			throw t;
@@ -288,6 +342,15 @@ public class EaglerTextureAtlasSpritePBR extends EaglerTextureAtlasSprite {
 		}
 	}
 
+	public void copyAnimationFrame(int mapWidth, int mapHeight, int mapLevel) {
+		Throwable t = new UnsupportedOperationException("Cannot call regular copyAnimationFrame in PBR mode, use updateAnimationPBR");
+		try {
+			throw t;
+		}catch(Throwable tt) {
+			logger.error(t);
+		}
+	}
+
 	protected void resetSprite() {
 		this.animationMetadata = null;
 		this.setFramesTextureDataPBR(new List[] { Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList() });
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/PBRTextureMapUtils.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/PBRTextureMapUtils.java
index e29938ff..065958a0 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/PBRTextureMapUtils.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/PBRTextureMapUtils.java
@@ -27,7 +27,7 @@ import net.minecraft.util.ResourceLocation;
  */
 public class PBRTextureMapUtils {
 
-	public static final ImageData defaultNormalsTexture = new ImageData(1, 1, new int[] { 0 }, true);
+	public static final ImageData defaultNormalsTexture = new ImageData(1, 1, new int[] { 0xFFFF7F7F }, true);
 
 	public static final PBRMaterialConstants blockMaterialConstants = new PBRMaterialConstants(new ResourceLocation("eagler:glsl/deferred/material_block_constants.csv"));
 
@@ -125,13 +125,17 @@ public class PBRTextureMapUtils {
 		}
 	}
 
-	public static ImageData generateMaterialTextureFor(String iconName) {
+	public static ImageData generateMaterialTextureFor(String iconName, String iconName2) {
 		if(iconName.startsWith("minecraft:")) {
 			iconName = iconName.substring(10);
 		}
 		Integer in = blockMaterialConstants.spriteNameToMaterialConstants.get(iconName);
 		if(in == null) {
-			return new ImageData(1, 1, new int[] { blockMaterialConstants.defaultMaterial }, true);
+			if(iconName2 != null) {
+				return generateMaterialTextureFor(iconName2, null);
+			}else {
+				return new ImageData(1, 1, new int[] { blockMaterialConstants.defaultMaterial }, true);
+			}
 		}else {
 			return new ImageData(1, 1, new int[] { in.intValue() }, true);
 		}
@@ -150,8 +154,8 @@ public class PBRTextureMapUtils {
 					ret[i] = new int[len];
 					int x, y, s1, s2, s3, s4, c1, c2, c3, c4;
 					for(int j = 0; j < len; ++j) {
-						x = (j % len) << 1;
-						y = (j / len) << 1;
+						x = (j % lvlW) << 1;
+						y = (j / lvlW) << 1;
 						s1 = ret[i - 1][x + y * lvl2W];
 						s2 = ret[i - 1][x + y * lvl2W + 1];
 						s3 = ret[i - 1][x + y * lvl2W + lvl2W];
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/TextureClockPBRImpl.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/TextureClockPBRImpl.java
index 620ad6d8..c7d4321d 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/TextureClockPBRImpl.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/TextureClockPBRImpl.java
@@ -27,7 +27,7 @@ public class TextureClockPBRImpl extends EaglerTextureAtlasSpritePBR {
 		super(spriteName);
 	}
 
-	public void updateAnimationPBR(IFramebufferGL[] copyColorFramebuffer, IFramebufferGL[] copyMaterialFramebuffer, int materialTexOffset) {
+	public void updateAnimationPBR() {
 		if (!this.frameTextureDataPBR[0].isEmpty()) {
 			Minecraft minecraft = Minecraft.getMinecraft();
 			double d0 = 0.0;
@@ -59,16 +59,32 @@ public class TextureClockPBRImpl extends EaglerTextureAtlasSpritePBR {
 
 			if (i != this.frameCounter) {
 				this.frameCounter = i;
-				animationCachePBR[0].copyFrameLevelsToTex2D(this.frameCounter, this.originX, this.originY, this.width,
-						this.height, copyColorFramebuffer);
-				if (!dontAnimateNormals)
-					animationCachePBR[1].copyFrameLevelsToTex2D(this.frameCounter, this.originX, this.originY,
-							this.width, this.height, copyMaterialFramebuffer);
-				if (!dontAnimateMaterial)
-					animationCachePBR[2].copyFrameLevelsToTex2D(this.frameCounter, this.originX,
-							this.originY + materialTexOffset, this.width, this.height, copyMaterialFramebuffer);
+				currentAnimUpdater = (mapWidth, mapHeight, mapLevel) -> {
+					animationCachePBR[0].copyFrameToTex2D(this.frameCounter, mapLevel, this.originX >> mapLevel,
+							this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+							mapHeight);
+				};
+				if(!dontAnimateNormals || !dontAnimateMaterial) {
+					currentAnimUpdaterPBR = (mapWidth, mapHeight, mapLevel) -> {
+						if (!dontAnimateNormals)
+							animationCachePBR[1].copyFrameToTex2D(this.frameCounter, mapLevel, this.originX,
+									this.originY, this.width, this.height, mapWidth, mapHeight);
+						if (!dontAnimateMaterial)
+							animationCachePBR[2].copyFrameToTex2D(this.frameCounter, mapLevel, this.originX >> mapLevel,
+									(this.originY >> mapLevel) + (mapHeight >> 1), this.width >> mapLevel,
+									this.height >> mapLevel, mapWidth, mapHeight);
+					};
+				}else {
+					currentAnimUpdaterPBR = null;
+				}
+			}else {
+				currentAnimUpdater = null;
+				currentAnimUpdaterPBR = null;
 			}
 
+		}else {
+			currentAnimUpdater = null;
+			currentAnimUpdaterPBR = null;
 		}
 	}
 
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/TextureCompassPBRImpl.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/TextureCompassPBRImpl.java
index b4ac23a2..7f1f97b1 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/TextureCompassPBRImpl.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/TextureCompassPBRImpl.java
@@ -29,18 +29,17 @@ public class TextureCompassPBRImpl extends EaglerTextureAtlasSpritePBR {
 		super(spriteName);
 	}
 
-	public void updateAnimationPBR(IFramebufferGL[] copyColorFramebuffer, IFramebufferGL[] copyMaterialFramebuffer, int materialOffset) {
+	public void updateAnimationPBR() {
 		Minecraft minecraft = Minecraft.getMinecraft();
 		if (minecraft.theWorld != null && minecraft.thePlayer != null) {
 			this.updateCompassPBR(minecraft.theWorld, minecraft.thePlayer.posX, minecraft.thePlayer.posZ,
-					(double) minecraft.thePlayer.rotationYaw, false, copyColorFramebuffer, copyMaterialFramebuffer, materialOffset);
+					(double) minecraft.thePlayer.rotationYaw, false);
 		} else {
-			this.updateCompassPBR((World) null, 0.0, 0.0, 0.0, true, copyColorFramebuffer, copyMaterialFramebuffer, materialOffset);
+			this.updateCompassPBR((World) null, 0.0, 0.0, 0.0, true);
 		}
 	}
 
-	public void updateCompassPBR(World worldIn, double playerX, double playerY, double playerZ, boolean noWorld,
-			IFramebufferGL[] copyColorFramebuffer, IFramebufferGL[] copyMaterialFramebuffer, int materialOffset) {
+	public void updateCompassPBR(World worldIn, double playerX, double playerY, double playerZ, boolean noWorld) {
 		if (!this.frameTextureDataPBR[0].isEmpty()) {
 			double d0 = 0.0;
 			if (worldIn != null && !noWorld) {
@@ -76,15 +75,33 @@ public class TextureCompassPBRImpl extends EaglerTextureAtlasSpritePBR {
 
 			if (i != this.frameCounter) {
 				this.frameCounter = i;
-				animationCachePBR[0].copyFrameLevelsToTex2D(this.frameCounter, this.originX, this.originY, this.width,
-						this.height, copyColorFramebuffer);
-				if (!dontAnimateNormals)
-					animationCachePBR[1].copyFrameLevelsToTex2D(this.frameCounter, this.originX, this.originY,
-							this.width, this.height, copyMaterialFramebuffer);
-				if (!dontAnimateMaterial)
-					animationCachePBR[2].copyFrameLevelsToTex2D(this.frameCounter, this.originX,
-							this.originY + materialOffset, this.width, this.height, copyMaterialFramebuffer);
+				currentAnimUpdater = (mapWidth, mapHeight, mapLevel) -> {
+					animationCachePBR[0].copyFrameToTex2D(this.frameCounter, mapLevel, this.originX >> mapLevel,
+							this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+							mapHeight);
+				};
+				if(!dontAnimateNormals || !dontAnimateMaterial) {
+					currentAnimUpdaterPBR = (mapWidth, mapHeight, mapLevel) -> {
+						if (!dontAnimateNormals)
+							animationCachePBR[1].copyFrameToTex2D(this.frameCounter, mapLevel, this.originX >> mapLevel,
+									this.originY >> mapLevel, this.width >> mapLevel, this.height >> mapLevel, mapWidth,
+									mapHeight);
+						if (!dontAnimateMaterial)
+							animationCachePBR[2].copyFrameToTex2D(this.frameCounter, mapLevel, this.originX >> mapLevel,
+									(this.originY >> mapLevel) + (mapHeight >> 1), this.width >> mapLevel,
+									this.height >> mapLevel, mapWidth, mapHeight);
+					};
+				}else {
+					currentAnimUpdaterPBR = null;
+				}
+			}else {
+				currentAnimUpdater = null;
+				currentAnimUpdaterPBR = null;
 			}
+
+		}else {
+			currentAnimUpdater = null;
+			currentAnimUpdaterPBR = null;
 		}
 	}
 
diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerChunkLoader.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerChunkLoader.java
index c8a8a9e2..17e079df 100644
--- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerChunkLoader.java
+++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerChunkLoader.java
@@ -88,6 +88,7 @@ public class EaglerChunkLoader extends AnvilChunkLoader {
 
 	@Override
 	public void saveChunk(World var1, Chunk var2) throws IOException {
+		var1.alfheim$getLightingEngine().processLightUpdates();
 		NBTTagCompound chunkData = new NBTTagCompound();
 		this.writeChunkToNBT(var2, var1, chunkData);
 		NBTTagCompound fileData = new NBTTagCompound();
diff --git a/sources/main/java/net/optifine/BetterGrass.java b/sources/main/java/net/optifine/BetterGrass.java
new file mode 100644
index 00000000..f9892db9
--- /dev/null
+++ b/sources/main/java/net/optifine/BetterGrass.java
@@ -0,0 +1,243 @@
+package net.optifine;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Properties;
+
+import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockDirt;
+import net.minecraft.block.BlockGrass;
+import net.minecraft.block.BlockMycelium;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.init.Blocks;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.world.IBlockAccess;
+import net.optifine.model.BlockModelUtils;
+import net.optifine.util.PropertiesOrdered;
+
+public class BetterGrass {
+	private static boolean betterGrass = true;
+	private static boolean betterMycelium = true;
+	private static boolean betterPodzol = true;
+	private static boolean betterGrassSnow = true;
+	private static boolean betterMyceliumSnow = true;
+	private static boolean betterPodzolSnow = true;
+	private static boolean grassMultilayer = false;
+	private static EaglerTextureAtlasSprite spriteGrass = null;
+	private static EaglerTextureAtlasSprite spriteGrassSide = null;
+	private static EaglerTextureAtlasSprite spriteMycelium = null;
+	private static EaglerTextureAtlasSprite spritePodzol = null;
+	private static EaglerTextureAtlasSprite spriteSnow = null;
+	private static boolean spritesLoaded = false;
+	private static IBakedModel modelCubeGrass = null;
+	private static IBakedModel modelCubeMycelium = null;
+	private static IBakedModel modelCubePodzol = null;
+	private static IBakedModel modelCubeSnow = null;
+	private static boolean modelsLoaded = false;
+	private static final String TEXTURE_GRASS_DEFAULT = "blocks/grass_top";
+	private static final String TEXTURE_GRASS_SIDE_DEFAULT = "blocks/grass_side";
+	private static final String TEXTURE_MYCELIUM_DEFAULT = "blocks/mycelium_top";
+	private static final String TEXTURE_PODZOL_DEFAULT = "blocks/dirt_podzol_top";
+	private static final String TEXTURE_SNOW_DEFAULT = "blocks/snow";
+
+	public static void updateIcons(TextureMap textureMap) {
+		spritesLoaded = false;
+		modelsLoaded = false;
+		loadProperties(textureMap);
+	}
+
+	public static void update() {
+		if (spritesLoaded) {
+			modelCubeGrass = BlockModelUtils.makeModelCube(spriteGrass, 0);
+
+			if (grassMultilayer) {
+				IBakedModel ibakedmodel = BlockModelUtils.makeModelCube(spriteGrassSide, -1);
+				modelCubeGrass = BlockModelUtils.joinModelsCube(ibakedmodel, modelCubeGrass);
+			}
+
+			modelCubeMycelium = BlockModelUtils.makeModelCube(spriteMycelium, -1);
+			modelCubePodzol = BlockModelUtils.makeModelCube(spritePodzol, 0);
+			modelCubeSnow = BlockModelUtils.makeModelCube(spriteSnow, -1);
+			modelsLoaded = true;
+		}
+	}
+
+	private static void loadProperties(TextureMap textureMap) {
+		betterGrass = true;
+		betterMycelium = true;
+		betterPodzol = true;
+		betterGrassSnow = true;
+		betterMyceliumSnow = true;
+		betterPodzolSnow = true;
+		spriteGrass = textureMap.registerSprite(new ResourceLocation("blocks/grass_top"));
+		spriteGrassSide = textureMap.registerSprite(new ResourceLocation("blocks/grass_side"));
+		spriteMycelium = textureMap.registerSprite(new ResourceLocation("blocks/mycelium_top"));
+		spritePodzol = textureMap.registerSprite(new ResourceLocation("blocks/dirt_podzol_top"));
+		spriteSnow = textureMap.registerSprite(new ResourceLocation("blocks/snow"));
+		spritesLoaded = true;
+		String s = "optifine/bettergrass.properties";
+
+		try (InputStream inputstream = Minecraft.getMinecraft().getResourceManager()
+				.getResource(new ResourceLocation(s)).getInputStream()) {
+			Properties properties = new PropertiesOrdered();
+			properties.load(inputstream);
+			inputstream.close();
+			betterGrass = getBoolean(properties, "grass", true);
+			betterMycelium = getBoolean(properties, "mycelium", true);
+			betterPodzol = getBoolean(properties, "podzol", true);
+			betterGrassSnow = getBoolean(properties, "grass.snow", true);
+			betterMyceliumSnow = getBoolean(properties, "mycelium.snow", true);
+			betterPodzolSnow = getBoolean(properties, "podzol.snow", true);
+			grassMultilayer = getBoolean(properties, "grass.multilayer", false);
+			spriteGrass = registerSprite(properties, "texture.grass", "blocks/grass_top", textureMap);
+			spriteGrassSide = registerSprite(properties, "texture.grass_side", "blocks/grass_side", textureMap);
+			spriteMycelium = registerSprite(properties, "texture.mycelium", "blocks/mycelium_top", textureMap);
+			spritePodzol = registerSprite(properties, "texture.podzol", "blocks/dirt_podzol_top", textureMap);
+			spriteSnow = registerSprite(properties, "texture.snow", "blocks/snow", textureMap);
+		} catch (IOException ioexception) {
+			Config.warn(
+					"Error reading: " + s + ", " + ioexception.getClass().getName() + ": " + ioexception.getMessage());
+		}
+	}
+
+	private static EaglerTextureAtlasSprite registerSprite(Properties props, String key, String textureDefault,
+			TextureMap textureMap) {
+		String s = props.getProperty(key);
+
+		if (s == null) {
+			s = textureDefault;
+		}
+
+//		ResourceLocation resourcelocation = new ResourceLocation("textures/" + s + ".png");
+//
+//		if (!Config.hasResource(resourcelocation)) {
+//			Config.warn("BetterGrass texture not found: " + resourcelocation);
+//			s = textureDefault;
+//		}
+
+		ResourceLocation resourcelocation1 = new ResourceLocation(s);
+		EaglerTextureAtlasSprite textureatlassprite = textureMap.registerSprite(resourcelocation1, key);
+		return textureatlassprite;
+	}
+
+	public static List getFaceQuads(IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos,
+			EnumFacing facing, List quads) {
+		if (facing != EnumFacing.UP && facing != EnumFacing.DOWN) {
+			if (!modelsLoaded) {
+				return quads;
+			} else {
+				Block block = blockState.getBlock();
+				return block instanceof BlockMycelium
+						? getFaceQuadsMycelium(blockAccess, blockState, blockPos, facing, quads)
+						: (block instanceof BlockDirt
+								? getFaceQuadsDirt(blockAccess, blockState, blockPos, facing, quads)
+								: (block instanceof BlockGrass
+										? getFaceQuadsGrass(blockAccess, blockState, blockPos, facing, quads)
+										: quads));
+			}
+		} else {
+			return quads;
+		}
+	}
+
+	private static List getFaceQuadsMycelium(IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos,
+			EnumFacing facing, List quads) {
+		Block block = blockAccess.getBlockState(blockPos.up()).getBlock();
+		boolean flag = block == Blocks.snow || block == Blocks.snow_layer;
+
+		if (Config.isBetterGrassFancy()) {
+			if (flag) {
+				if (betterMyceliumSnow && getBlockAt(blockPos, facing, blockAccess) == Blocks.snow_layer) {
+					return modelCubeSnow.getFaceQuads(facing);
+				}
+			} else if (betterMycelium && getBlockAt(blockPos.down(), facing, blockAccess) == Blocks.mycelium) {
+				return modelCubeMycelium.getFaceQuads(facing);
+			}
+		} else if (flag) {
+			if (betterMyceliumSnow) {
+				return modelCubeSnow.getFaceQuads(facing);
+			}
+		} else if (betterMycelium) {
+			return modelCubeMycelium.getFaceQuads(facing);
+		}
+
+		return quads;
+	}
+
+	private static List getFaceQuadsDirt(IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos,
+			EnumFacing facing, List quads) {
+		Block block = getBlockAt(blockPos, EnumFacing.UP, blockAccess);
+
+		if (blockState.getValue(BlockDirt.VARIANT) != BlockDirt.DirtType.PODZOL) {
+			return quads;
+		} else {
+			boolean flag = block == Blocks.snow || block == Blocks.snow_layer;
+
+			if (Config.isBetterGrassFancy()) {
+				if (flag) {
+					if (betterPodzolSnow && getBlockAt(blockPos, facing, blockAccess) == Blocks.snow_layer) {
+						return modelCubeSnow.getFaceQuads(facing);
+					}
+				} else if (betterPodzol) {
+					BlockPos blockpos = blockPos.down().offset(facing);
+					IBlockState iblockstate = blockAccess.getBlockState(blockpos);
+
+					if (iblockstate.getBlock() == Blocks.dirt
+							&& iblockstate.getValue(BlockDirt.VARIANT) == BlockDirt.DirtType.PODZOL) {
+						return modelCubePodzol.getFaceQuads(facing);
+					}
+				}
+			} else if (flag) {
+				if (betterPodzolSnow) {
+					return modelCubeSnow.getFaceQuads(facing);
+				}
+			} else if (betterPodzol) {
+				return modelCubePodzol.getFaceQuads(facing);
+			}
+
+			return quads;
+		}
+	}
+
+	private static List getFaceQuadsGrass(IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos,
+			EnumFacing facing, List quads) {
+		Block block = blockAccess.getBlockState(blockPos.up()).getBlock();
+		boolean flag = block == Blocks.snow || block == Blocks.snow_layer;
+
+		if (Config.isBetterGrassFancy()) {
+			if (flag) {
+				if (betterGrassSnow && getBlockAt(blockPos, facing, blockAccess) == Blocks.snow_layer) {
+					return modelCubeSnow.getFaceQuads(facing);
+				}
+			} else if (betterGrass && getBlockAt(blockPos.down(), facing, blockAccess) == Blocks.grass) {
+				return modelCubeGrass.getFaceQuads(facing);
+			}
+		} else if (flag) {
+			if (betterGrassSnow) {
+				return modelCubeSnow.getFaceQuads(facing);
+			}
+		} else if (betterGrass) {
+			return modelCubeGrass.getFaceQuads(facing);
+		}
+
+		return quads;
+	}
+
+	private static Block getBlockAt(BlockPos blockPos, EnumFacing facing, IBlockAccess blockAccess) {
+		BlockPos blockpos = blockPos.offset(facing);
+		Block block = blockAccess.getBlockState(blockpos).getBlock();
+		return block;
+	}
+
+	private static boolean getBoolean(Properties props, String key, boolean def) {
+		String s = props.getProperty(key);
+		return s == null ? def : Boolean.parseBoolean(s);
+	}
+}
diff --git a/sources/main/java/net/optifine/BetterSnow.java b/sources/main/java/net/optifine/BetterSnow.java
new file mode 100644
index 00000000..4ba85cde
--- /dev/null
+++ b/sources/main/java/net/optifine/BetterSnow.java
@@ -0,0 +1,91 @@
+package net.optifine;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockBush;
+import net.minecraft.block.BlockDoublePlant;
+import net.minecraft.block.BlockFence;
+import net.minecraft.block.BlockFenceGate;
+import net.minecraft.block.BlockFlower;
+import net.minecraft.block.BlockFlowerPot;
+import net.minecraft.block.BlockLever;
+import net.minecraft.block.BlockMushroom;
+import net.minecraft.block.BlockPane;
+import net.minecraft.block.BlockRedstoneTorch;
+import net.minecraft.block.BlockReed;
+import net.minecraft.block.BlockSapling;
+import net.minecraft.block.BlockSnow;
+import net.minecraft.block.BlockTallGrass;
+import net.minecraft.block.BlockTorch;
+import net.minecraft.block.BlockWall;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.init.Blocks;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.world.IBlockAccess;
+
+public class BetterSnow {
+	private static IBakedModel modelSnowLayer = null;
+
+	public static void update() {
+		modelSnowLayer = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes()
+				.getModelForState(Blocks.snow_layer.getDefaultState());
+	}
+
+	public static IBakedModel getModelSnowLayer() {
+		return modelSnowLayer;
+	}
+
+	public static IBlockState getStateSnowLayer() {
+		return Blocks.snow_layer.getDefaultState();
+	}
+
+	public static boolean shouldRender(IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos) {
+		Block block = blockState.getBlock();
+		return !checkBlock(block, blockState) ? false : hasSnowNeighbours(blockAccess, blockPos);
+	}
+
+	private static boolean hasSnowNeighbours(IBlockAccess blockAccess, BlockPos pos) {
+		Block block = Blocks.snow_layer;
+		return blockAccess.getBlockState(pos.north()).getBlock() != block
+				&& blockAccess.getBlockState(pos.south()).getBlock() != block
+				&& blockAccess.getBlockState(pos.west()).getBlock() != block
+				&& blockAccess.getBlockState(pos.east()).getBlock() != block ? false
+						: blockAccess.getBlockState(pos.down()).getBlock().isOpaqueCube();
+	}
+
+	private static boolean checkBlock(Block block, IBlockState blockState) {
+		if (block.isFullCube()) {
+			return false;
+		} else if (block.isOpaqueCube()) {
+			return false;
+		} else if (block instanceof BlockSnow) {
+			return false;
+		} else if (!(block instanceof BlockBush) || !(block instanceof BlockDoublePlant)
+				&& !(block instanceof BlockFlower) && !(block instanceof BlockMushroom)
+				&& !(block instanceof BlockSapling) && !(block instanceof BlockTallGrass)) {
+			if (!(block instanceof BlockFence) && !(block instanceof BlockFenceGate)
+					&& !(block instanceof BlockFlowerPot) && !(block instanceof BlockPane)
+					&& !(block instanceof BlockReed) && !(block instanceof BlockWall)) {
+				if (block instanceof BlockRedstoneTorch && blockState.getValue(BlockTorch.FACING) == EnumFacing.UP) {
+					return true;
+				} else {
+					if (block instanceof BlockLever) {
+						Object object = blockState.getValue(BlockLever.FACING);
+
+						if (object == BlockLever.EnumOrientation.UP_X || object == BlockLever.EnumOrientation.UP_Z) {
+							return true;
+						}
+					}
+
+					return false;
+				}
+			} else {
+				return true;
+			}
+		} else {
+			return true;
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/BlockDir.java b/sources/main/java/net/optifine/BlockDir.java
new file mode 100644
index 00000000..71742435
--- /dev/null
+++ b/sources/main/java/net/optifine/BlockDir.java
@@ -0,0 +1,78 @@
+package net.optifine;
+
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+
+public enum BlockDir {
+	DOWN(EnumFacing.DOWN), UP(EnumFacing.UP), NORTH(EnumFacing.NORTH), SOUTH(EnumFacing.SOUTH), WEST(EnumFacing.WEST),
+	EAST(EnumFacing.EAST), NORTH_WEST(EnumFacing.NORTH, EnumFacing.WEST), NORTH_EAST(EnumFacing.NORTH, EnumFacing.EAST),
+	SOUTH_WEST(EnumFacing.SOUTH, EnumFacing.WEST), SOUTH_EAST(EnumFacing.SOUTH, EnumFacing.EAST),
+	DOWN_NORTH(EnumFacing.DOWN, EnumFacing.NORTH), DOWN_SOUTH(EnumFacing.DOWN, EnumFacing.SOUTH),
+	UP_NORTH(EnumFacing.UP, EnumFacing.NORTH), UP_SOUTH(EnumFacing.UP, EnumFacing.SOUTH),
+	DOWN_WEST(EnumFacing.DOWN, EnumFacing.WEST), DOWN_EAST(EnumFacing.DOWN, EnumFacing.EAST),
+	UP_WEST(EnumFacing.UP, EnumFacing.WEST), UP_EAST(EnumFacing.UP, EnumFacing.EAST);
+
+	private EnumFacing facing1;
+	private EnumFacing facing2;
+
+	private BlockDir(EnumFacing facing1) {
+		this.facing1 = facing1;
+	}
+
+	private BlockDir(EnumFacing facing1, EnumFacing facing2) {
+		this.facing1 = facing1;
+		this.facing2 = facing2;
+	}
+
+	public EnumFacing getFacing1() {
+		return this.facing1;
+	}
+
+	public EnumFacing getFacing2() {
+		return this.facing2;
+	}
+
+	BlockPos offset(BlockPos pos) {
+		pos = pos.offset(this.facing1, 1);
+
+		if (this.facing2 != null) {
+			pos = pos.offset(this.facing2, 1);
+		}
+
+		return pos;
+	}
+
+	public int getOffsetX() {
+		int i = this.facing1.getFrontOffsetX();
+
+		if (this.facing2 != null) {
+			i += this.facing2.getFrontOffsetX();
+		}
+
+		return i;
+	}
+
+	public int getOffsetY() {
+		int i = this.facing1.getFrontOffsetY();
+
+		if (this.facing2 != null) {
+			i += this.facing2.getFrontOffsetY();
+		}
+
+		return i;
+	}
+
+	public int getOffsetZ() {
+		int i = this.facing1.getFrontOffsetZ();
+
+		if (this.facing2 != null) {
+			i += this.facing2.getFrontOffsetZ();
+		}
+
+		return i;
+	}
+
+	public boolean isDouble() {
+		return this.facing2 != null;
+	}
+}
diff --git a/sources/main/java/net/optifine/Config.java b/sources/main/java/net/optifine/Config.java
new file mode 100644
index 00000000..3c0fb325
--- /dev/null
+++ b/sources/main/java/net/optifine/Config.java
@@ -0,0 +1,339 @@
+package net.optifine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
+import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.resources.DefaultResourcePack;
+import net.minecraft.client.resources.IResourcePack;
+import net.minecraft.client.resources.ResourcePackRepository;
+import net.minecraft.client.settings.GameSettings;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.ResourceLocation;
+import net.optifine.util.TextureUtils;
+
+public class Config {
+
+	private static final Logger logger = LogManager.getLogger("OptiFine");
+
+	private static Minecraft mc;
+	private static GameSettings gameSettings;
+
+	public static void setGameObj(Minecraft mc) {
+		Config.mc = mc;
+		Config.gameSettings = mc.gameSettings;
+	}
+
+	public static String[] tokenize(String p_tokenize_0_, String p_tokenize_1_) {
+		StringTokenizer stringtokenizer = new StringTokenizer(p_tokenize_0_, p_tokenize_1_);
+		List list = new ArrayList();
+
+		while (stringtokenizer.hasMoreTokens()) {
+			String s = stringtokenizer.nextToken();
+			list.add(s);
+		}
+
+		String[] astring = (String[]) list.toArray(new String[list.size()]);
+		return astring;
+	}
+
+	public static boolean isCustomSky() {
+		return gameSettings.customSkyOF;
+	}
+
+	public static boolean isBetterGrass() {
+		return gameSettings.betterGrassOF != 0;
+	}
+
+	public static boolean isBetterGrassFancy() {
+		return gameSettings.betterGrassOF == 2;
+	}
+
+	public static boolean isBetterSnow() {
+		return gameSettings.betterGrassOF != 0;
+	}
+
+	public static boolean isConnectedTextures() {
+		return gameSettings.connectedTexturesOF != 0;
+	}
+
+	public static boolean isConnectedTexturesFancy() {
+		return gameSettings.connectedTexturesOF == 2;
+	}
+
+	public static boolean isTreesFancy() {
+		return gameSettings.fancyGraphics;
+	}
+
+	public static boolean isTreesSmart() {
+		return gameSettings.fancyGraphics && gameSettings.smartLeavesOF;
+	}
+
+	public static boolean isCullFacesLeaves() {
+		return !gameSettings.fancyGraphics;
+	}
+
+	public static boolean isCustomItems() {
+		return gameSettings.customItemsOF;
+	}
+
+	public static void dbg(String string) {
+		logger.debug(string);
+	}
+
+	public static void warn(String string) {
+		logger.warn(string);
+	}
+
+	public static void error(String string) {
+		logger.error(string);
+	}
+
+	public static int limit(int p_limit_0_, int p_limit_1_, int p_limit_2_) {
+		return p_limit_0_ < p_limit_1_ ? p_limit_1_ : (p_limit_0_ > p_limit_2_ ? p_limit_2_ : p_limit_0_);
+	}
+
+	public static float limit(float p_limit_0_, float p_limit_1_, float p_limit_2_) {
+		return p_limit_0_ < p_limit_1_ ? p_limit_1_ : (p_limit_0_ > p_limit_2_ ? p_limit_2_ : p_limit_0_);
+	}
+
+	public static double limit(double p_limit_0_, double p_limit_2_, double p_limit_4_) {
+		return p_limit_0_ < p_limit_2_ ? p_limit_2_ : (p_limit_0_ > p_limit_4_ ? p_limit_4_ : p_limit_0_);
+	}
+
+	public static int parseInt(String p_parseInt_0_, int p_parseInt_1_) {
+		try {
+			if (p_parseInt_0_ == null) {
+				return p_parseInt_1_;
+			} else {
+				p_parseInt_0_ = p_parseInt_0_.trim();
+				return Integer.parseInt(p_parseInt_0_);
+			}
+		} catch (NumberFormatException var3) {
+			return p_parseInt_1_;
+		}
+	}
+
+	public static float parseFloat(String p_parseFloat_0_, float p_parseFloat_1_) {
+		try {
+			if (p_parseFloat_0_ == null) {
+				return p_parseFloat_1_;
+			} else {
+				p_parseFloat_0_ = p_parseFloat_0_.trim();
+				return Float.parseFloat(p_parseFloat_0_);
+			}
+		} catch (NumberFormatException var3) {
+			return p_parseFloat_1_;
+		}
+	}
+
+	public static boolean parseBoolean(String p_parseBoolean_0_, boolean p_parseBoolean_1_) {
+		try {
+			if (p_parseBoolean_0_ == null) {
+				return p_parseBoolean_1_;
+			} else {
+				p_parseBoolean_0_ = p_parseBoolean_0_.trim();
+				return Boolean.parseBoolean(p_parseBoolean_0_);
+			}
+		} catch (NumberFormatException var3) {
+			return p_parseBoolean_1_;
+		}
+	}
+
+	public static int[] addIntToArray(int[] p_addIntToArray_0_, int p_addIntToArray_1_) {
+		if (p_addIntToArray_0_ != null) {
+			int[] ret = new int[p_addIntToArray_0_.length + 1];
+			System.arraycopy(p_addIntToArray_0_, 0, ret, 0, p_addIntToArray_0_.length);
+			ret[p_addIntToArray_0_.length] = p_addIntToArray_1_;
+			return ret;
+		} else {
+			throw new NullPointerException("The given array is NULL");
+		}
+	}
+
+	public static int[] addIntsToArray(int[] p_addIntsToArray_0_, int[] p_addIntsToArray_1_) {
+		if (p_addIntsToArray_0_ != null && p_addIntsToArray_1_ != null) {
+			int i = p_addIntsToArray_0_.length;
+			int j = i + p_addIntsToArray_1_.length;
+			int[] aint = new int[j];
+			System.arraycopy(p_addIntsToArray_0_, 0, aint, 0, i);
+
+			for (int k = 0; k < p_addIntsToArray_1_.length; ++k) {
+				aint[k + i] = p_addIntsToArray_1_[k];
+			}
+
+			return aint;
+		} else {
+			throw new NullPointerException("The given array is NULL");
+		}
+	}
+
+	public static String arrayToString(Object[] p_arrayToString_0_) {
+		return arrayToString(p_arrayToString_0_, ", ");
+	}
+
+	public static String arrayToString(Object[] p_arrayToString_0_, String p_arrayToString_1_) {
+		if (p_arrayToString_0_ == null) {
+			return "";
+		} else {
+			StringBuffer stringbuffer = new StringBuffer(p_arrayToString_0_.length * 5);
+
+			for (int i = 0; i < p_arrayToString_0_.length; ++i) {
+				Object object = p_arrayToString_0_[i];
+
+				if (i > 0) {
+					stringbuffer.append(p_arrayToString_1_);
+				}
+
+				stringbuffer.append(String.valueOf(object));
+			}
+
+			return stringbuffer.toString();
+		}
+	}
+
+	public static String arrayToString(int[] p_arrayToString_0_) {
+		return arrayToString(p_arrayToString_0_, ", ");
+	}
+
+	public static String arrayToString(int[] p_arrayToString_0_, String p_arrayToString_1_) {
+		if (p_arrayToString_0_ == null) {
+			return "";
+		} else {
+			StringBuffer stringbuffer = new StringBuffer(p_arrayToString_0_.length * 5);
+
+			for (int i = 0; i < p_arrayToString_0_.length; ++i) {
+				int j = p_arrayToString_0_[i];
+
+				if (i > 0) {
+					stringbuffer.append(p_arrayToString_1_);
+				}
+
+				stringbuffer.append(String.valueOf(j));
+			}
+
+			return stringbuffer.toString();
+		}
+	}
+
+	public static int[] toPrimitive(Integer[] p_toPrimitive_0_) {
+		if (p_toPrimitive_0_ == null) {
+			return null;
+		} else if (p_toPrimitive_0_.length == 0) {
+			return new int[0];
+		} else {
+			int[] aint = new int[p_toPrimitive_0_.length];
+
+			for (int i = 0; i < aint.length; ++i) {
+				aint[i] = p_toPrimitive_0_[i].intValue();
+			}
+
+			return aint;
+		}
+	}
+
+	public static boolean isSameOne(Object p_isSameOne_0_, Object[] p_isSameOne_1_) {
+		if (p_isSameOne_1_ == null) {
+			return false;
+		} else {
+			for (int i = 0; i < p_isSameOne_1_.length; ++i) {
+				Object object = p_isSameOne_1_[i];
+
+				if (p_isSameOne_0_ == object) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+
+	public static boolean equals(Object p_equals_0_, Object p_equals_1_) {
+		return p_equals_0_ == p_equals_1_ ? true : (p_equals_0_ == null ? false : p_equals_0_.equals(p_equals_1_));
+	}
+
+	public static boolean isFromDefaultResourcePack(ResourceLocation p_isFromDefaultResourcePack_0_) {
+		IResourcePack iresourcepack = getDefiningResourcePack(p_isFromDefaultResourcePack_0_);
+		return iresourcepack == mc.getDefaultResourcePack();
+	}
+
+	public static IResourcePack getDefiningResourcePack(ResourceLocation p_getDefiningResourcePack_0_) {
+		ResourcePackRepository resourcepackrepository = mc.getResourcePackRepository();
+		IResourcePack iresourcepack = resourcepackrepository.getResourcePackInstance();
+
+		if (iresourcepack != null && iresourcepack.resourceExists(p_getDefiningResourcePack_0_)) {
+			return iresourcepack;
+		} else {
+			List<ResourcePackRepository.Entry> list = resourcepackrepository.getRepositoryEntries();
+
+			for (int i = list.size() - 1; i >= 0; --i) {
+				ResourcePackRepository.Entry resourcepackrepository$entry = (ResourcePackRepository.Entry) list.get(i);
+				IResourcePack iresourcepack1 = resourcepackrepository$entry.getResourcePack();
+
+				if (iresourcepack1.resourceExists(p_getDefiningResourcePack_0_)) {
+					return iresourcepack1;
+				}
+			}
+
+			DefaultResourcePack res = mc.getDefaultResourcePack();
+			if (res.resourceExists(p_getDefiningResourcePack_0_)) {
+				return res;
+			} else {
+				return null;
+			}
+		}
+	}
+
+	public static boolean hasResource(ResourceLocation p_hasResource_0_) {
+		if (p_hasResource_0_ == null) {
+			return false;
+		} else {
+			IResourcePack iresourcepack = getDefiningResourcePack(p_hasResource_0_);
+			return iresourcepack != null;
+		}
+	}
+
+	public static IResourcePack[] getResourcePacks() {
+		ResourcePackRepository resourcepackrepository = mc.getResourcePackRepository();
+		List list = resourcepackrepository.getRepositoryEntries();
+		List list1 = new ArrayList();
+
+		for (Object resourcepackrepository$entry0 : list) {
+			ResourcePackRepository.Entry resourcepackrepository$entry = (ResourcePackRepository.Entry) resourcepackrepository$entry0;
+			list1.add(resourcepackrepository$entry.getResourcePack());
+		}
+
+		if (resourcepackrepository.getResourcePackInstance() != null) {
+			list1.add(resourcepackrepository.getResourcePackInstance());
+		}
+
+		IResourcePack[] airesourcepack = (IResourcePack[]) ((IResourcePack[]) list1
+				.toArray(new IResourcePack[list1.size()]));
+		return airesourcepack;
+	}
+
+	public static int intHash(int p_intHash_0_) {
+		p_intHash_0_ = p_intHash_0_ ^ 61 ^ p_intHash_0_ >> 16;
+		p_intHash_0_ = p_intHash_0_ + (p_intHash_0_ << 3);
+		p_intHash_0_ = p_intHash_0_ ^ p_intHash_0_ >> 4;
+		p_intHash_0_ = p_intHash_0_ * 668265261;
+		p_intHash_0_ = p_intHash_0_ ^ p_intHash_0_ >> 15;
+		return p_intHash_0_;
+	}
+
+	public static int getRandom(BlockPos p_getRandom_0_, int p_getRandom_1_) {
+		int i = intHash(p_getRandom_1_ + 37);
+		i = intHash(i + p_getRandom_0_.getX());
+		i = intHash(i + p_getRandom_0_.getZ());
+		i = intHash(i + p_getRandom_0_.getY());
+		return i;
+	}
+
+	public static void frameInitHook() {
+		TextureUtils.registerResourceListener();
+	}
+
+}
diff --git a/sources/main/java/net/optifine/ConnectedProperties.java b/sources/main/java/net/optifine/ConnectedProperties.java
new file mode 100644
index 00000000..c91405de
--- /dev/null
+++ b/sources/main/java/net/optifine/ConnectedProperties.java
@@ -0,0 +1,993 @@
+package net.optifine;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Function;
+
+import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
+import net.minecraft.block.Block;
+import net.minecraft.block.properties.IProperty;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.init.Blocks;
+import net.minecraft.util.EnumWorldBlockLayer;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.optifine.config.ConnectedParser;
+import net.optifine.config.MatchBlock;
+import net.optifine.config.Matches;
+import net.optifine.config.NbtTagValue;
+import net.optifine.config.RangeInt;
+import net.optifine.config.RangeListInt;
+import net.optifine.util.MathUtils;
+import net.optifine.util.TextureUtils;
+
+public class ConnectedProperties {
+	public String name = null;
+	public String basePath = null;
+	public MatchBlock[] matchBlocks = null;
+	public int[] metadatas = null;
+	public String[] matchTiles = null;
+	public int method = 0;
+	public String[] tiles = null;
+	public int connect = 0;
+	public int faces = 63;
+	public BiomeGenBase[] biomes = null;
+	public RangeListInt heights = null;
+	public int renderPass = 0;
+	public boolean innerSeams = false;
+	public int[] ctmTileIndexes = null;
+	public int width = 0;
+	public int height = 0;
+	public int[] weights = null;
+	public int randomLoops = 0;
+	public int symmetry = 1;
+	public boolean linked = false;
+	public NbtTagValue nbtName = null;
+	public int[] sumWeights = null;
+	public int sumAllWeights = 1;
+	public EaglerTextureAtlasSprite[] matchTileIcons = null;
+	public EaglerTextureAtlasSprite[] tileIcons = null;
+	public MatchBlock[] connectBlocks = null;
+	public String[] connectTiles = null;
+	public EaglerTextureAtlasSprite[] connectTileIcons = null;
+	public int tintIndex = -1;
+	public IBlockState tintBlockState = Blocks.air.getDefaultState();
+	public EnumWorldBlockLayer layer = null;
+	public static final int METHOD_NONE = 0;
+	public static final int METHOD_CTM = 1;
+	public static final int METHOD_HORIZONTAL = 2;
+	public static final int METHOD_TOP = 3;
+	public static final int METHOD_RANDOM = 4;
+	public static final int METHOD_REPEAT = 5;
+	public static final int METHOD_VERTICAL = 6;
+	public static final int METHOD_FIXED = 7;
+	public static final int METHOD_HORIZONTAL_VERTICAL = 8;
+	public static final int METHOD_VERTICAL_HORIZONTAL = 9;
+	public static final int METHOD_CTM_COMPACT = 10;
+	public static final int METHOD_OVERLAY = 11;
+	public static final int METHOD_OVERLAY_FIXED = 12;
+	public static final int METHOD_OVERLAY_RANDOM = 13;
+	public static final int METHOD_OVERLAY_REPEAT = 14;
+	public static final int METHOD_OVERLAY_CTM = 15;
+	public static final int CONNECT_NONE = 0;
+	public static final int CONNECT_BLOCK = 1;
+	public static final int CONNECT_TILE = 2;
+	public static final int CONNECT_MATERIAL = 3;
+	public static final int CONNECT_UNKNOWN = 128;
+	public static final int FACE_BOTTOM = 1;
+	public static final int FACE_TOP = 2;
+	public static final int FACE_NORTH = 4;
+	public static final int FACE_SOUTH = 8;
+	public static final int FACE_WEST = 16;
+	public static final int FACE_EAST = 32;
+	public static final int FACE_SIDES = 60;
+	public static final int FACE_ALL = 63;
+	public static final int FACE_UNKNOWN = 128;
+	public static final int SYMMETRY_NONE = 1;
+	public static final int SYMMETRY_OPPOSITE = 2;
+	public static final int SYMMETRY_ALL = 6;
+	public static final int SYMMETRY_UNKNOWN = 128;
+	public static final String TILE_SKIP_PNG = "<skip>.png";
+	public static final String TILE_DEFAULT_PNG = "<default>.png";
+
+	public ConnectedProperties(Properties props, String path) {
+		ConnectedParser connectedparser = new ConnectedParser("ConnectedTextures");
+		this.name = connectedparser.parseName(path);
+		this.basePath = connectedparser.parseBasePath(path);
+		this.matchBlocks = connectedparser.parseMatchBlocks(props.getProperty("matchBlocks"));
+		this.metadatas = connectedparser.parseIntList(props.getProperty("metadata"));
+		this.matchTiles = this.parseMatchTiles(props.getProperty("matchTiles"));
+		this.method = parseMethod(props.getProperty("method"));
+		this.tiles = this.parseTileNames(props.getProperty("tiles"));
+		this.connect = parseConnect(props.getProperty("connect"));
+		this.faces = parseFaces(props.getProperty("faces"));
+		this.biomes = connectedparser.parseBiomes(props.getProperty("biomes"));
+		this.heights = connectedparser.parseRangeListInt(props.getProperty("heights"));
+
+		if (this.heights == null) {
+			int i = connectedparser.parseInt(props.getProperty("minHeight"), -1);
+			int j = connectedparser.parseInt(props.getProperty("maxHeight"), 1024);
+
+			if (i != -1 || j != 1024) {
+				this.heights = new RangeListInt(new RangeInt(i, j));
+			}
+		}
+
+		this.renderPass = connectedparser.parseInt(props.getProperty("renderPass"), -1);
+		this.innerSeams = connectedparser.parseBoolean(props.getProperty("innerSeams"), false);
+		this.ctmTileIndexes = this.parseCtmTileIndexes(props);
+		this.width = connectedparser.parseInt(props.getProperty("width"), -1);
+		this.height = connectedparser.parseInt(props.getProperty("height"), -1);
+		this.weights = connectedparser.parseIntList(props.getProperty("weights"));
+		this.randomLoops = connectedparser.parseInt(props.getProperty("randomLoops"), 0);
+		this.symmetry = parseSymmetry(props.getProperty("symmetry"));
+		this.linked = connectedparser.parseBoolean(props.getProperty("linked"), false);
+		this.nbtName = connectedparser.parseNbtTagValue("name", props.getProperty("name"));
+		this.connectBlocks = connectedparser.parseMatchBlocks(props.getProperty("connectBlocks"));
+		this.connectTiles = this.parseMatchTiles(props.getProperty("connectTiles"));
+		this.tintIndex = connectedparser.parseInt(props.getProperty("tintIndex"), -1);
+		this.tintBlockState = connectedparser.parseBlockState(props.getProperty("tintBlock"),
+				Blocks.air.getDefaultState());
+		this.layer = connectedparser.parseBlockRenderLayer(props.getProperty("layer"),
+				EnumWorldBlockLayer.CUTOUT_MIPPED);
+	}
+
+	private int[] parseCtmTileIndexes(Properties props) {
+		if (this.tiles == null) {
+			return null;
+		} else {
+			Map<Integer, Integer> map = new HashMap();
+
+			for (Object object : props.keySet()) {
+				if (object instanceof String) {
+					String s = (String) object;
+					String s1 = "ctm.";
+
+					if (s.startsWith(s1)) {
+						String s2 = s.substring(s1.length());
+						String s3 = props.getProperty(s);
+
+						if (s3 != null) {
+							s3 = s3.trim();
+							int i = Config.parseInt(s2, -1);
+
+							if (i >= 0 && i <= 46) {
+								int j = Config.parseInt(s3, -1);
+
+								if (j >= 0 && j < this.tiles.length) {
+									map.put(Integer.valueOf(i), Integer.valueOf(j));
+								} else {
+									Config.warn("Invalid CTM tile index: " + s3);
+								}
+							} else {
+								Config.warn("Invalid CTM index: " + s2);
+							}
+						}
+					}
+				}
+			}
+
+			if (map.isEmpty()) {
+				return null;
+			} else {
+				int[] aint = new int[47];
+
+				for (int k = 0; k < aint.length; ++k) {
+					aint[k] = -1;
+
+					if (map.containsKey(Integer.valueOf(k))) {
+						aint[k] = ((Integer) map.get(Integer.valueOf(k))).intValue();
+					}
+				}
+
+				return aint;
+			}
+		}
+	}
+
+	private String[] parseMatchTiles(String str) {
+		if (str == null) {
+			return null;
+		} else {
+			String[] astring = Config.tokenize(str, " ");
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+
+				if (s.endsWith(".png")) {
+					s = s.substring(0, s.length() - 4);
+				}
+
+				s = TextureUtils.fixResourcePath(s, this.basePath);
+				astring[i] = s;
+			}
+
+			return astring;
+		}
+	}
+
+	private static String parseName(String path) {
+		String s = path;
+		int i = path.lastIndexOf(47);
+
+		if (i >= 0) {
+			s = path.substring(i + 1);
+		}
+
+		int j = s.lastIndexOf(46);
+
+		if (j >= 0) {
+			s = s.substring(0, j);
+		}
+
+		return s;
+	}
+
+	private static String parseBasePath(String path) {
+		int i = path.lastIndexOf(47);
+		return i < 0 ? "" : path.substring(0, i);
+	}
+
+	private String[] parseTileNames(String str) {
+		if (str == null) {
+			return null;
+		} else {
+			List list = new ArrayList();
+			String[] astring = Config.tokenize(str, " ,");
+			label32:
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+
+				if (s.contains("-")) {
+					String[] astring1 = Config.tokenize(s, "-");
+
+					if (astring1.length == 2) {
+						int j = Config.parseInt(astring1[0], -1);
+						int k = Config.parseInt(astring1[1], -1);
+
+						if (j >= 0 && k >= 0) {
+							if (j > k) {
+								Config.warn("Invalid interval: " + s + ", when parsing: " + str);
+								continue;
+							}
+
+							int l = j;
+
+							while (true) {
+								if (l > k) {
+									continue label32;
+								}
+
+								list.add(String.valueOf(l));
+								++l;
+							}
+						}
+					}
+				}
+
+				list.add(s);
+			}
+
+			String[] astring2 = (String[]) list.toArray(new String[list.size()]);
+
+			for (int i1 = 0; i1 < astring2.length; ++i1) {
+				String s1 = astring2[i1];
+				s1 = TextureUtils.fixResourcePath(s1, this.basePath);
+
+				if (!s1.startsWith(this.basePath) && !s1.startsWith("textures/") && !s1.startsWith("mcpatcher/")) {
+					s1 = this.basePath + "/" + s1;
+				}
+
+				if (s1.endsWith(".png")) {
+					s1 = s1.substring(0, s1.length() - 4);
+				}
+
+				if (s1.startsWith("/")) {
+					s1 = s1.substring(1);
+				}
+
+				astring2[i1] = s1;
+			}
+
+			return astring2;
+		}
+	}
+
+	private static int parseSymmetry(String str) {
+		if (str == null) {
+			return 1;
+		} else {
+			str = str.trim();
+
+			if (str.equals("opposite")) {
+				return 2;
+			} else if (str.equals("all")) {
+				return 6;
+			} else {
+				Config.warn("Unknown symmetry: " + str);
+				return 1;
+			}
+		}
+	}
+
+	private static int parseFaces(String str) {
+		if (str == null) {
+			return 63;
+		} else {
+			String[] astring = Config.tokenize(str, " ,");
+			int i = 0;
+
+			for (int j = 0; j < astring.length; ++j) {
+				String s = astring[j];
+				int k = parseFace(s);
+				i |= k;
+			}
+
+			return i;
+		}
+	}
+
+	private static int parseFace(String str) {
+		str = str.toLowerCase();
+
+		if (!str.equals("bottom") && !str.equals("down")) {
+			if (!str.equals("top") && !str.equals("up")) {
+				if (str.equals("north")) {
+					return 4;
+				} else if (str.equals("south")) {
+					return 8;
+				} else if (str.equals("east")) {
+					return 32;
+				} else if (str.equals("west")) {
+					return 16;
+				} else if (str.equals("sides")) {
+					return 60;
+				} else if (str.equals("all")) {
+					return 63;
+				} else {
+					Config.warn("Unknown face: " + str);
+					return 128;
+				}
+			} else {
+				return 2;
+			}
+		} else {
+			return 1;
+		}
+	}
+
+	private static int parseConnect(String str) {
+		if (str == null) {
+			return 0;
+		} else {
+			str = str.trim();
+
+			if (str.equals("block")) {
+				return 1;
+			} else if (str.equals("tile")) {
+				return 2;
+			} else if (str.equals("material")) {
+				return 3;
+			} else {
+				Config.warn("Unknown connect: " + str);
+				return 128;
+			}
+		}
+	}
+
+	public static IProperty getProperty(String key, Collection properties) {
+		for (Object iproperty0 : properties) {
+			IProperty iproperty = (IProperty) iproperty0;
+			if (key.equals(iproperty.getName())) {
+				return iproperty;
+			}
+		}
+
+		return null;
+	}
+
+	private static int parseMethod(String str) {
+		if (str == null) {
+			return 1;
+		} else {
+			str = str.trim();
+
+			if (!str.equals("ctm") && !str.equals("glass")) {
+				if (str.equals("ctm_compact")) {
+					return 10;
+				} else if (!str.equals("horizontal") && !str.equals("bookshelf")) {
+					if (str.equals("vertical")) {
+						return 6;
+					} else if (str.equals("top")) {
+						return 3;
+					} else if (str.equals("random")) {
+						return 4;
+					} else if (str.equals("repeat")) {
+						return 5;
+					} else if (str.equals("fixed")) {
+						return 7;
+					} else if (!str.equals("horizontal+vertical") && !str.equals("h+v")) {
+						if (!str.equals("vertical+horizontal") && !str.equals("v+h")) {
+							if (str.equals("overlay")) {
+								return 11;
+							} else if (str.equals("overlay_fixed")) {
+								return 12;
+							} else if (str.equals("overlay_random")) {
+								return 13;
+							} else if (str.equals("overlay_repeat")) {
+								return 14;
+							} else if (str.equals("overlay_ctm")) {
+								return 15;
+							} else {
+								Config.warn("Unknown method: " + str);
+								return 0;
+							}
+						} else {
+							return 9;
+						}
+					} else {
+						return 8;
+					}
+				} else {
+					return 2;
+				}
+			} else {
+				return 1;
+			}
+		}
+	}
+
+	public boolean isValid(String path) {
+		if (this.name != null && this.name.length() > 0) {
+			if (this.basePath == null) {
+				Config.warn("No base path found: " + path);
+				return false;
+			} else {
+				if (this.matchBlocks == null) {
+					this.matchBlocks = this.detectMatchBlocks();
+				}
+
+				if (this.matchTiles == null && this.matchBlocks == null) {
+					this.matchTiles = this.detectMatchTiles();
+				}
+
+				if (this.matchBlocks == null && this.matchTiles == null) {
+					Config.warn("No matchBlocks or matchTiles specified: " + path);
+					return false;
+				} else if (this.method == 0) {
+					Config.warn("No method: " + path);
+					return false;
+				} else if (this.tiles != null && this.tiles.length > 0) {
+					if (this.connect == 0) {
+						this.connect = this.detectConnect();
+					}
+
+					if (this.connect == 128) {
+						Config.warn("Invalid connect in: " + path);
+						return false;
+					} else if (this.renderPass > 0) {
+						Config.warn("Render pass not supported: " + this.renderPass);
+						return false;
+					} else if ((this.faces & 128) != 0) {
+						Config.warn("Invalid faces in: " + path);
+						return false;
+					} else if ((this.symmetry & 128) != 0) {
+						Config.warn("Invalid symmetry in: " + path);
+						return false;
+					} else {
+						switch (this.method) {
+						case 1:
+							return this.isValidCtm(path);
+
+						case 2:
+							return this.isValidHorizontal(path);
+
+						case 3:
+							return this.isValidTop(path);
+
+						case 4:
+							return this.isValidRandom(path);
+
+						case 5:
+							return this.isValidRepeat(path);
+
+						case 6:
+							return this.isValidVertical(path);
+
+						case 7:
+							return this.isValidFixed(path);
+
+						case 8:
+							return this.isValidHorizontalVertical(path);
+
+						case 9:
+							return this.isValidVerticalHorizontal(path);
+
+						case 10:
+							return this.isValidCtmCompact(path);
+
+						case 11:
+							return this.isValidOverlay(path);
+
+						case 12:
+							return this.isValidOverlayFixed(path);
+
+						case 13:
+							return this.isValidOverlayRandom(path);
+
+						case 14:
+							return this.isValidOverlayRepeat(path);
+
+						case 15:
+							return this.isValidOverlayCtm(path);
+
+						default:
+							Config.warn("Unknown method: " + path);
+							return false;
+						}
+					}
+				} else {
+					Config.warn("No tiles specified: " + path);
+					return false;
+				}
+			}
+		} else {
+			Config.warn("No name found: " + path);
+			return false;
+		}
+	}
+
+	private int detectConnect() {
+		return this.matchBlocks != null ? 1 : (this.matchTiles != null ? 2 : 128);
+	}
+
+	private MatchBlock[] detectMatchBlocks() {
+		int[] aint = this.detectMatchBlockIds();
+
+		if (aint == null) {
+			return null;
+		} else {
+			MatchBlock[] amatchblock = new MatchBlock[aint.length];
+
+			for (int i = 0; i < amatchblock.length; ++i) {
+				amatchblock[i] = new MatchBlock(aint[i]);
+			}
+
+			return amatchblock;
+		}
+	}
+
+	private int[] detectMatchBlockIds() {
+		if (!this.name.startsWith("block")) {
+			return null;
+		} else {
+			int i = "block".length();
+			int j;
+
+			for (j = i; j < this.name.length(); ++j) {
+				char c0 = this.name.charAt(j);
+
+				if (c0 < 48 || c0 > 57) {
+					break;
+				}
+			}
+
+			if (j == i) {
+				return null;
+			} else {
+				String s = this.name.substring(i, j);
+				int k = Config.parseInt(s, -1);
+				return k < 0 ? null : new int[] { k };
+			}
+		}
+	}
+
+	private String[] detectMatchTiles() {
+		EaglerTextureAtlasSprite textureatlassprite = getIcon(this.name);
+		return textureatlassprite == null ? null : new String[] { this.name };
+	}
+
+	private static EaglerTextureAtlasSprite getIcon(String iconName) {
+		TextureMap texturemap = Minecraft.getMinecraft().getTextureMapBlocks();
+		EaglerTextureAtlasSprite textureatlassprite = texturemap.getSpriteSafe(iconName);
+
+		if (textureatlassprite != null) {
+			return textureatlassprite;
+		} else {
+			textureatlassprite = texturemap.getSpriteSafe("blocks/" + iconName);
+			return textureatlassprite;
+		}
+	}
+
+	private boolean isValidCtm(String path) {
+		if (this.tiles == null) {
+			this.tiles = this.parseTileNames("0-11 16-27 32-43 48-58");
+		}
+
+		if (this.tiles.length < 47) {
+			Config.warn("Invalid tiles, must be at least 47: " + path);
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private boolean isValidCtmCompact(String path) {
+		if (this.tiles == null) {
+			this.tiles = this.parseTileNames("0-4");
+		}
+
+		if (this.tiles.length < 5) {
+			Config.warn("Invalid tiles, must be at least 5: " + path);
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private boolean isValidOverlay(String path) {
+		if (this.tiles == null) {
+			this.tiles = this.parseTileNames("0-16");
+		}
+
+		if (this.tiles.length < 17) {
+			Config.warn("Invalid tiles, must be at least 17: " + path);
+			return false;
+		} else if (this.layer != null && this.layer != EnumWorldBlockLayer.SOLID) {
+			return true;
+		} else {
+			Config.warn("Invalid overlay layer: " + this.layer);
+			return false;
+		}
+	}
+
+	private boolean isValidOverlayFixed(String path) {
+		if (!this.isValidFixed(path)) {
+			return false;
+		} else if (this.layer != null && this.layer != EnumWorldBlockLayer.SOLID) {
+			return true;
+		} else {
+			Config.warn("Invalid overlay layer: " + this.layer);
+			return false;
+		}
+	}
+
+	private boolean isValidOverlayRandom(String path) {
+		if (!this.isValidRandom(path)) {
+			return false;
+		} else if (this.layer != null && this.layer != EnumWorldBlockLayer.SOLID) {
+			return true;
+		} else {
+			Config.warn("Invalid overlay layer: " + this.layer);
+			return false;
+		}
+	}
+
+	private boolean isValidOverlayRepeat(String path) {
+		if (!this.isValidRepeat(path)) {
+			return false;
+		} else if (this.layer != null && this.layer != EnumWorldBlockLayer.SOLID) {
+			return true;
+		} else {
+			Config.warn("Invalid overlay layer: " + this.layer);
+			return false;
+		}
+	}
+
+	private boolean isValidOverlayCtm(String path) {
+		if (!this.isValidCtm(path)) {
+			return false;
+		} else if (this.layer != null && this.layer != EnumWorldBlockLayer.SOLID) {
+			return true;
+		} else {
+			Config.warn("Invalid overlay layer: " + this.layer);
+			return false;
+		}
+	}
+
+	private boolean isValidHorizontal(String path) {
+		if (this.tiles == null) {
+			this.tiles = this.parseTileNames("12-15");
+		}
+
+		if (this.tiles.length != 4) {
+			Config.warn("Invalid tiles, must be exactly 4: " + path);
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private boolean isValidVertical(String path) {
+		if (this.tiles == null) {
+			Config.warn("No tiles defined for vertical: " + path);
+			return false;
+		} else if (this.tiles.length != 4) {
+			Config.warn("Invalid tiles, must be exactly 4: " + path);
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private boolean isValidHorizontalVertical(String path) {
+		if (this.tiles == null) {
+			Config.warn("No tiles defined for horizontal+vertical: " + path);
+			return false;
+		} else if (this.tiles.length != 7) {
+			Config.warn("Invalid tiles, must be exactly 7: " + path);
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private boolean isValidVerticalHorizontal(String path) {
+		if (this.tiles == null) {
+			Config.warn("No tiles defined for vertical+horizontal: " + path);
+			return false;
+		} else if (this.tiles.length != 7) {
+			Config.warn("Invalid tiles, must be exactly 7: " + path);
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private boolean isValidRandom(String path) {
+		if (this.tiles != null && this.tiles.length > 0) {
+			if (this.weights != null) {
+				if (this.weights.length > this.tiles.length) {
+					Config.warn("More weights defined than tiles, trimming weights: " + path);
+					int[] aint = new int[this.tiles.length];
+					System.arraycopy(this.weights, 0, aint, 0, aint.length);
+					this.weights = aint;
+				}
+
+				if (this.weights.length < this.tiles.length) {
+					Config.warn("Less weights defined than tiles, expanding weights: " + path);
+					int[] aint1 = new int[this.tiles.length];
+					System.arraycopy(this.weights, 0, aint1, 0, this.weights.length);
+					int i = MathUtils.getAverage(this.weights);
+
+					for (int j = this.weights.length; j < aint1.length; ++j) {
+						aint1[j] = i;
+					}
+
+					this.weights = aint1;
+				}
+
+				this.sumWeights = new int[this.weights.length];
+				int k = 0;
+
+				for (int l = 0; l < this.weights.length; ++l) {
+					k += this.weights[l];
+					this.sumWeights[l] = k;
+				}
+
+				this.sumAllWeights = k;
+
+				if (this.sumAllWeights <= 0) {
+					Config.warn("Invalid sum of all weights: " + k);
+					this.sumAllWeights = 1;
+				}
+			}
+
+			if (this.randomLoops >= 0 && this.randomLoops <= 9) {
+				return true;
+			} else {
+				Config.warn("Invalid randomLoops: " + this.randomLoops);
+				return false;
+			}
+		} else {
+			Config.warn("Tiles not defined: " + path);
+			return false;
+		}
+	}
+
+	private boolean isValidRepeat(String path) {
+		if (this.tiles == null) {
+			Config.warn("Tiles not defined: " + path);
+			return false;
+		} else if (this.width <= 0) {
+			Config.warn("Invalid width: " + path);
+			return false;
+		} else if (this.height <= 0) {
+			Config.warn("Invalid height: " + path);
+			return false;
+		} else if (this.tiles.length != this.width * this.height) {
+			Config.warn("Number of tiles does not equal width x height: " + path);
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private boolean isValidFixed(String path) {
+		if (this.tiles == null) {
+			Config.warn("Tiles not defined: " + path);
+			return false;
+		} else if (this.tiles.length != 1) {
+			Config.warn("Number of tiles should be 1 for method: fixed.");
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private boolean isValidTop(String path) {
+		if (this.tiles == null) {
+			this.tiles = this.parseTileNames("66");
+		}
+
+		if (this.tiles.length != 1) {
+			Config.warn("Invalid tiles, must be exactly 1: " + path);
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	public void updateIcons(TextureMap textureMap) {
+		String baseName = null;
+		if (this.matchTiles != null) {
+			this.matchTileIcons = registerIcons(this.matchTiles, null, textureMap, false, false);
+			if(this.matchTiles.length > 0) {
+				baseName = "blocks/" + this.matchTiles[0];
+			}
+		}
+
+		if(baseName == null) {
+			int blockBaseID = -1;
+			int metaBase = -1;
+			if(this.matchBlocks != null && this.matchBlocks.length > 0) {
+				MatchBlock b = this.matchBlocks[0];
+				blockBaseID = b.getBlockId();
+				int[] metas = b.getMetadatas();
+				if(metas != null && metas.length > 0) {
+					metaBase = metas[0];
+				}
+			}
+			if(blockBaseID != -1) {
+				if(metaBase == -1 && this.metadatas != null && this.metadatas.length > 0) {
+					metaBase = this.metadatas[0];
+				}
+				if(metaBase == -1) {
+					metaBase = 0;
+				}
+				baseName = Minecraft.getMinecraft().getModelManager().modelbakerytmp.getBaseTextureForBlockPre(blockBaseID, metaBase);
+			}
+		}
+
+		if (this.connectTiles != null) {
+			this.connectTileIcons = registerIcons(this.connectTiles, baseName, textureMap, false, false);
+		}
+
+		if (this.tiles != null) {
+			this.tileIcons = registerIcons(this.tiles, baseName, textureMap, true, !isMethodOverlay(this.method));
+		}
+	}
+
+	private static boolean isMethodOverlay(int method) {
+		switch (method) {
+		case 11:
+		case 12:
+		case 13:
+		case 14:
+		case 15:
+			return true;
+
+		default:
+			return false;
+		}
+	}
+
+	private static EaglerTextureAtlasSprite[] registerIcons(String[] tileNames, String baseName,
+			TextureMap textureMap, boolean skipTiles, boolean defaultTiles) {
+		if (tileNames == null) {
+			return null;
+		} else {
+			List list = new ArrayList();
+
+			for (int i = 0; i < tileNames.length; ++i) {
+				String s = tileNames[i];
+				ResourceLocation resourcelocation = new ResourceLocation(s);
+				String s1 = resourcelocation.getResourceDomain();
+				String s2 = resourcelocation.getResourcePath();
+
+				if (!s2.contains("/")) {
+					s2 = "textures/blocks/" + s2;
+				}
+
+				String s3 = s2 + ".png";
+
+				if (skipTiles && s3.endsWith("<skip>.png")) {
+					list.add(null);
+				} else if (defaultTiles && s3.endsWith("<default>.png")) {
+					list.add(ConnectedTextures.SPRITE_DEFAULT);
+				} else {
+					ResourceLocation resourcelocation1 = new ResourceLocation(s1, s3);
+					boolean flag = Config.hasResource(resourcelocation1);
+
+					if (!flag) {
+						Config.warn("File not found: " + s3);
+					}
+
+					String s4 = "textures/";
+					String s5 = s2;
+
+					if (s2.startsWith(s4)) {
+						s5 = s2.substring(s4.length());
+					}
+
+					ResourceLocation resourcelocation2 = new ResourceLocation(s1, s5);
+					EaglerTextureAtlasSprite textureatlassprite = textureMap.registerSprite(resourcelocation2, baseName);
+					list.add(textureatlassprite);
+				}
+			}
+
+			EaglerTextureAtlasSprite[] atextureatlassprite = (EaglerTextureAtlasSprite[]) list
+					.toArray(new EaglerTextureAtlasSprite[list.size()]);
+			return atextureatlassprite;
+		}
+	}
+
+	public boolean matchesBlockId(int blockId) {
+		return Matches.blockId(blockId, this.matchBlocks);
+	}
+
+	public boolean matchesBlock(int blockId, int metadata) {
+		return !Matches.block(blockId, metadata, this.matchBlocks) ? false : Matches.metadata(metadata, this.metadatas);
+	}
+
+	public boolean matchesIcon(EaglerTextureAtlasSprite icon) {
+		return Matches.sprite(icon, this.matchTileIcons);
+	}
+
+	public String toString() {
+		return "CTM name: " + this.name + ", basePath: " + this.basePath + ", matchBlocks: "
+				+ Config.arrayToString((Object[]) this.matchBlocks) + ", matchTiles: "
+				+ Config.arrayToString((Object[]) this.matchTiles);
+	}
+
+	public boolean matchesBiome(BiomeGenBase biome) {
+		return Matches.biome(biome, this.biomes);
+	}
+
+	public int getMetadataMax() {
+		int i = -1;
+		i = this.getMax(this.metadatas, i);
+
+		if (this.matchBlocks != null) {
+			for (int j = 0; j < this.matchBlocks.length; ++j) {
+				MatchBlock matchblock = this.matchBlocks[j];
+				i = this.getMax(matchblock.getMetadatas(), i);
+			}
+		}
+
+		return i;
+	}
+
+	private int getMax(int[] mds, int max) {
+		if (mds == null) {
+			return max;
+		} else {
+			for (int i = 0; i < mds.length; ++i) {
+				int j = mds[i];
+
+				if (j > max) {
+					max = j;
+				}
+			}
+
+			return max;
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/ConnectedTextures.java b/sources/main/java/net/optifine/ConnectedTextures.java
new file mode 100644
index 00000000..374da246
--- /dev/null
+++ b/sources/main/java/net/optifine/ConnectedTextures.java
@@ -0,0 +1,2102 @@
+package net.optifine;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import net.lax1dude.eaglercraft.v1_8.IOUtils;
+import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockGlass;
+import net.minecraft.block.BlockPane;
+import net.minecraft.block.BlockQuartz;
+import net.minecraft.block.BlockRotatedPillar;
+import net.minecraft.block.BlockStainedGlass;
+import net.minecraft.block.BlockStainedGlassPane;
+import net.minecraft.block.state.BlockStateBase;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.client.resources.IResourcePack;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.init.Blocks;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.optifine.config.Matches;
+import net.optifine.model.BlockModelUtils;
+import net.optifine.model.ListQuadsOverlay;
+import net.optifine.render.RenderEnv;
+import net.optifine.util.PropertiesOrdered;
+import net.optifine.util.ResUtils;
+import net.optifine.util.TileEntityUtils;
+
+public class ConnectedTextures {
+	private static Map[] spriteQuadMaps = null;
+	private static Map[] spriteQuadFullMaps = null;
+	private static Map[][] spriteQuadCompactMaps = (Map[][]) null;
+	private static ConnectedProperties[][] blockProperties = (ConnectedProperties[][]) null;
+	private static ConnectedProperties[][] tileProperties = (ConnectedProperties[][]) null;
+	private static boolean multipass = false;
+	protected static final int UNKNOWN = -1;
+	protected static final int Y_NEG_DOWN = 0;
+	protected static final int Y_POS_UP = 1;
+	protected static final int Z_NEG_NORTH = 2;
+	protected static final int Z_POS_SOUTH = 3;
+	protected static final int X_NEG_WEST = 4;
+	protected static final int X_POS_EAST = 5;
+	private static final int Y_AXIS = 0;
+	private static final int Z_AXIS = 1;
+	private static final int X_AXIS = 2;
+	public static final IBlockState AIR_DEFAULT_STATE = Blocks.air.getDefaultState();
+	private static EaglerTextureAtlasSprite emptySprite = null;
+	private static final BlockDir[] SIDES_Y_NEG_DOWN = new BlockDir[] { BlockDir.WEST, BlockDir.EAST, BlockDir.NORTH,
+			BlockDir.SOUTH };
+	private static final BlockDir[] SIDES_Y_POS_UP = new BlockDir[] { BlockDir.WEST, BlockDir.EAST, BlockDir.SOUTH,
+			BlockDir.NORTH };
+	private static final BlockDir[] SIDES_Z_NEG_NORTH = new BlockDir[] { BlockDir.EAST, BlockDir.WEST, BlockDir.DOWN,
+			BlockDir.UP };
+	private static final BlockDir[] SIDES_Z_POS_SOUTH = new BlockDir[] { BlockDir.WEST, BlockDir.EAST, BlockDir.DOWN,
+			BlockDir.UP };
+	private static final BlockDir[] SIDES_X_NEG_WEST = new BlockDir[] { BlockDir.NORTH, BlockDir.SOUTH, BlockDir.DOWN,
+			BlockDir.UP };
+	private static final BlockDir[] SIDES_X_POS_EAST = new BlockDir[] { BlockDir.SOUTH, BlockDir.NORTH, BlockDir.DOWN,
+			BlockDir.UP };
+	private static final BlockDir[] SIDES_Z_NEG_NORTH_Z_AXIS = new BlockDir[] { BlockDir.WEST, BlockDir.EAST,
+			BlockDir.UP, BlockDir.DOWN };
+	private static final BlockDir[] SIDES_X_POS_EAST_X_AXIS = new BlockDir[] { BlockDir.NORTH, BlockDir.SOUTH,
+			BlockDir.UP, BlockDir.DOWN };
+	private static final BlockDir[] EDGES_Y_NEG_DOWN = new BlockDir[] { BlockDir.NORTH_EAST, BlockDir.NORTH_WEST,
+			BlockDir.SOUTH_EAST, BlockDir.SOUTH_WEST };
+	private static final BlockDir[] EDGES_Y_POS_UP = new BlockDir[] { BlockDir.SOUTH_EAST, BlockDir.SOUTH_WEST,
+			BlockDir.NORTH_EAST, BlockDir.NORTH_WEST };
+	private static final BlockDir[] EDGES_Z_NEG_NORTH = new BlockDir[] { BlockDir.DOWN_WEST, BlockDir.DOWN_EAST,
+			BlockDir.UP_WEST, BlockDir.UP_EAST };
+	private static final BlockDir[] EDGES_Z_POS_SOUTH = new BlockDir[] { BlockDir.DOWN_EAST, BlockDir.DOWN_WEST,
+			BlockDir.UP_EAST, BlockDir.UP_WEST };
+	private static final BlockDir[] EDGES_X_NEG_WEST = new BlockDir[] { BlockDir.DOWN_SOUTH, BlockDir.DOWN_NORTH,
+			BlockDir.UP_SOUTH, BlockDir.UP_NORTH };
+	private static final BlockDir[] EDGES_X_POS_EAST = new BlockDir[] { BlockDir.DOWN_NORTH, BlockDir.DOWN_SOUTH,
+			BlockDir.UP_NORTH, BlockDir.UP_SOUTH };
+	private static final BlockDir[] EDGES_Z_NEG_NORTH_Z_AXIS = new BlockDir[] { BlockDir.UP_EAST, BlockDir.UP_WEST,
+			BlockDir.DOWN_EAST, BlockDir.DOWN_WEST };
+	private static final BlockDir[] EDGES_X_POS_EAST_X_AXIS = new BlockDir[] { BlockDir.UP_SOUTH, BlockDir.UP_NORTH,
+			BlockDir.DOWN_SOUTH, BlockDir.DOWN_NORTH };
+	public static final EaglerTextureAtlasSprite SPRITE_DEFAULT = new EaglerTextureAtlasSprite("<default>");
+
+	public static BakedQuad[] getConnectedTexture(IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos,
+			BakedQuad quad, RenderEnv renderEnv) {
+		EaglerTextureAtlasSprite textureatlassprite = quad.getSprite();
+
+		if (textureatlassprite == null) {
+			return renderEnv.getArrayQuadsCtm(quad);
+		} else {
+			Block block = blockState.getBlock();
+
+			if (skipConnectedTexture(blockAccess, blockState, blockPos, quad, renderEnv)) {
+				quad = getQuad(emptySprite, quad);
+				return renderEnv.getArrayQuadsCtm(quad);
+			} else {
+				EnumFacing enumfacing = quad.getFace();
+				BakedQuad[] abakedquad = getConnectedTextureMultiPass(blockAccess, blockState, blockPos, enumfacing,
+						quad, renderEnv);
+				return abakedquad;
+			}
+		}
+	}
+
+	private static boolean skipConnectedTexture(IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos,
+			BakedQuad quad, RenderEnv renderEnv) {
+		Block block = blockState.getBlock();
+
+		if (block instanceof BlockPane) {
+			EaglerTextureAtlasSprite textureatlassprite = quad.getSprite();
+
+			if (textureatlassprite.getIconName().startsWith("minecraft:blocks/glass_pane_top")) {
+				IBlockState iblockstate1 = blockAccess.getBlockState(blockPos.offset(quad.getFace()));
+				return iblockstate1 == blockState;
+			}
+		}
+
+		if (block instanceof BlockPane) {
+			EnumFacing enumfacing = quad.getFace();
+
+			if (enumfacing != EnumFacing.UP && enumfacing != EnumFacing.DOWN) {
+				return false;
+			}
+
+			if (!quad.isFaceQuad()) {
+				return false;
+			}
+
+			BlockPos blockpos = blockPos.offset(quad.getFace());
+			IBlockState iblockstate = blockAccess.getBlockState(blockpos);
+
+			if (iblockstate.getBlock() != block) {
+				return false;
+			}
+
+			if (block == Blocks.stained_glass_pane && iblockstate.getValue(BlockStainedGlassPane.COLOR) != blockState
+					.getValue(BlockStainedGlassPane.COLOR)) {
+				return false;
+			}
+
+			iblockstate = iblockstate.getBlock().getActualState(iblockstate, blockAccess, blockpos);
+			double d0 = (double) quad.getMidX();
+
+			if (d0 < 0.4D) {
+				if (((Boolean) iblockstate.getValue(BlockPane.WEST)).booleanValue()) {
+					return true;
+				}
+			} else if (d0 > 0.6D) {
+				if (((Boolean) iblockstate.getValue(BlockPane.EAST)).booleanValue()) {
+					return true;
+				}
+			} else {
+				double d1 = quad.getMidZ();
+
+				if (d1 < 0.4D) {
+					if (((Boolean) iblockstate.getValue(BlockPane.NORTH)).booleanValue()) {
+						return true;
+					}
+				} else {
+					if (d1 <= 0.6D) {
+						return true;
+					}
+
+					if (((Boolean) iblockstate.getValue(BlockPane.SOUTH)).booleanValue()) {
+						return true;
+					}
+				}
+			}
+		}
+
+		return false;
+	}
+
+	protected static BakedQuad[] getQuads(EaglerTextureAtlasSprite sprite, BakedQuad quadIn, RenderEnv renderEnv) {
+		if (sprite == null) {
+			return null;
+		} else if (sprite == SPRITE_DEFAULT) {
+			return renderEnv.getArrayQuadsCtm(quadIn);
+		} else {
+			BakedQuad bakedquad = getQuad(sprite, quadIn);
+			BakedQuad[] abakedquad = renderEnv.getArrayQuadsCtm(bakedquad);
+			return abakedquad;
+		}
+	}
+
+	private static synchronized BakedQuad getQuad(EaglerTextureAtlasSprite sprite, BakedQuad quadIn) {
+		if (spriteQuadMaps == null) {
+			return quadIn;
+		} else {
+			int i = sprite.getIndexInMap();
+
+			if (i >= 0 && i < spriteQuadMaps.length) {
+				Map map = spriteQuadMaps[i];
+
+				if (map == null) {
+					map = new IdentityHashMap(1);
+					spriteQuadMaps[i] = map;
+				}
+
+				BakedQuad bakedquad = (BakedQuad) map.get(quadIn);
+
+				if (bakedquad == null) {
+					bakedquad = makeSpriteQuad(quadIn, sprite);
+					map.put(quadIn, bakedquad);
+				}
+
+				return bakedquad;
+			} else {
+				return quadIn;
+			}
+		}
+	}
+
+	private static synchronized BakedQuad getQuadFull(EaglerTextureAtlasSprite sprite, BakedQuad quadIn,
+			int tintIndex) {
+		if (spriteQuadFullMaps == null) {
+			return null;
+		} else if (sprite == null) {
+			return null;
+		} else {
+			int i = sprite.getIndexInMap();
+
+			if (i >= 0 && i < spriteQuadFullMaps.length) {
+				Map map = spriteQuadFullMaps[i];
+
+				if (map == null) {
+					map = new EnumMap(EnumFacing.class);
+					spriteQuadFullMaps[i] = map;
+				}
+
+				EnumFacing enumfacing = quadIn.getFace();
+				BakedQuad bakedquad = (BakedQuad) map.get(enumfacing);
+
+				if (bakedquad == null) {
+					bakedquad = BlockModelUtils.makeBakedQuad(enumfacing, sprite, tintIndex);
+					map.put(enumfacing, bakedquad);
+				}
+
+				return bakedquad;
+			} else {
+				return null;
+			}
+		}
+	}
+
+	private static BakedQuad makeSpriteQuad(BakedQuad quad, EaglerTextureAtlasSprite sprite) {
+		int[] aint = (int[]) quad.getVertexData().clone();
+		int[] aint2 = (int[]) quad.getVertexDataWithNormals().clone();
+		EaglerTextureAtlasSprite textureatlassprite = quad.getSprite();
+
+		for (int i = 0; i < 4; ++i) {
+			fixVertex(aint, aint2, i, textureatlassprite, sprite);
+		}
+
+		BakedQuad bakedquad = new BakedQuad(aint, aint2, quad.getTintIndex(), quad.getFace(), sprite);
+		return bakedquad;
+	}
+
+	private static void fixVertex(int[] data, int[] data2, int vertex, EaglerTextureAtlasSprite spriteFrom,
+			EaglerTextureAtlasSprite spriteTo) {
+		int i = data.length / 4;
+		int j = i * vertex;
+		int i2 = data2.length / 4;
+		int j2 = i2 * vertex;
+		float f = Float.intBitsToFloat(data[j + 4]);
+		float f1 = Float.intBitsToFloat(data[j + 4 + 1]);
+		double d0 = spriteFrom.getSpriteU16(f);
+		double d1 = spriteFrom.getSpriteV16(f1);
+		data[j + 4] = data2[j2 + 4] = Float.floatToRawIntBits(spriteTo.getInterpolatedU(d0));
+		data[j + 4 + 1] = data2[j2 + 4 + 1] = Float.floatToRawIntBits(spriteTo.getInterpolatedV(d1));
+	}
+
+	private static BakedQuad[] getConnectedTextureMultiPass(IBlockAccess blockAccess, IBlockState blockState,
+			BlockPos blockPos, EnumFacing side, BakedQuad quad, RenderEnv renderEnv) {
+		BakedQuad[] abakedquad = getConnectedTextureSingle(blockAccess, blockState, blockPos, side, quad, true, 0,
+				renderEnv);
+
+		if (!multipass) {
+			return abakedquad;
+		} else if (abakedquad.length == 1 && abakedquad[0] == quad) {
+			return abakedquad;
+		} else {
+			List<BakedQuad> list = renderEnv.getListQuadsCtmMultipass(abakedquad);
+
+			for (int i = 0; i < list.size(); ++i) {
+				BakedQuad bakedquad = (BakedQuad) list.get(i);
+				BakedQuad bakedquad1 = bakedquad;
+
+				for (int j = 0; j < 3; ++j) {
+					BakedQuad[] abakedquad1 = getConnectedTextureSingle(blockAccess, blockState, blockPos, side,
+							bakedquad1, false, j + 1, renderEnv);
+
+					if (abakedquad1.length != 1 || abakedquad1[0] == bakedquad1) {
+						break;
+					}
+
+					bakedquad1 = abakedquad1[0];
+				}
+
+				list.set(i, bakedquad1);
+			}
+
+			for (int k = 0; k < abakedquad.length; ++k) {
+				abakedquad[k] = (BakedQuad) list.get(k);
+			}
+
+			return abakedquad;
+		}
+	}
+
+	public static BakedQuad[] getConnectedTextureSingle(IBlockAccess blockAccess, IBlockState blockState,
+			BlockPos blockPos, EnumFacing facing, BakedQuad quad, boolean checkBlocks, int pass, RenderEnv renderEnv) {
+		Block block = blockState.getBlock();
+
+		if (!(blockState instanceof BlockStateBase)) {
+			return renderEnv.getArrayQuadsCtm(quad);
+		} else {
+			BlockStateBase blockstatebase = (BlockStateBase) blockState;
+			EaglerTextureAtlasSprite textureatlassprite = quad.getSprite();
+
+			if (tileProperties != null) {
+				int i = textureatlassprite.getIndexInMap();
+
+				if (i >= 0 && i < tileProperties.length) {
+					ConnectedProperties[] aconnectedproperties = tileProperties[i];
+
+					if (aconnectedproperties != null) {
+						int j = getSide(facing);
+
+						for (int k = 0; k < aconnectedproperties.length; ++k) {
+							ConnectedProperties connectedproperties = aconnectedproperties[k];
+
+							if (connectedproperties != null
+									&& connectedproperties.matchesBlockId(blockstatebase.getBlockId())) {
+								BakedQuad[] abakedquad = getConnectedTexture(connectedproperties, blockAccess,
+										blockstatebase, blockPos, j, quad, pass, renderEnv);
+
+								if (abakedquad != null) {
+									return abakedquad;
+								}
+							}
+						}
+					}
+				}
+			}
+
+			if (blockProperties != null && checkBlocks) {
+				int l = renderEnv.getBlockId();
+
+				if (l >= 0 && l < blockProperties.length) {
+					ConnectedProperties[] aconnectedproperties1 = blockProperties[l];
+
+					if (aconnectedproperties1 != null) {
+						int i1 = getSide(facing);
+
+						for (int j1 = 0; j1 < aconnectedproperties1.length; ++j1) {
+							ConnectedProperties connectedproperties1 = aconnectedproperties1[j1];
+
+							if (connectedproperties1 != null && connectedproperties1.matchesIcon(textureatlassprite)) {
+								BakedQuad[] abakedquad1 = getConnectedTexture(connectedproperties1, blockAccess,
+										blockstatebase, blockPos, i1, quad, pass, renderEnv);
+
+								if (abakedquad1 != null) {
+									return abakedquad1;
+								}
+							}
+						}
+					}
+				}
+			}
+
+			return renderEnv.getArrayQuadsCtm(quad);
+		}
+	}
+
+	public static int getSide(EnumFacing facing) {
+		if (facing == null) {
+			return -1;
+		} else {
+			switch (facing) {
+			case DOWN:
+				return 0;
+
+			case UP:
+				return 1;
+
+			case EAST:
+				return 5;
+
+			case WEST:
+				return 4;
+
+			case NORTH:
+				return 2;
+
+			case SOUTH:
+				return 3;
+
+			default:
+				return -1;
+			}
+		}
+	}
+
+	private static EnumFacing getFacing(int side) {
+		switch (side) {
+		case 0:
+			return EnumFacing.DOWN;
+
+		case 1:
+			return EnumFacing.UP;
+
+		case 2:
+			return EnumFacing.NORTH;
+
+		case 3:
+			return EnumFacing.SOUTH;
+
+		case 4:
+			return EnumFacing.WEST;
+
+		case 5:
+			return EnumFacing.EAST;
+
+		default:
+			return EnumFacing.UP;
+		}
+	}
+
+	private static BakedQuad[] getConnectedTexture(ConnectedProperties cp, IBlockAccess blockAccess,
+			BlockStateBase blockState, BlockPos blockPos, int side, BakedQuad quad, int pass, RenderEnv renderEnv) {
+		int i = 0;
+		int j = blockState.getMetadata();
+		int k = j;
+		Block block = blockState.getBlock();
+
+		if (block instanceof BlockRotatedPillar) {
+			i = getWoodAxis(side, j);
+
+			if (cp.getMetadataMax() <= 3) {
+				k = j & 3;
+			}
+		}
+
+		if (block instanceof BlockQuartz) {
+			i = getQuartzAxis(side, j);
+
+			if (cp.getMetadataMax() <= 2 && k > 2) {
+				k = 2;
+			}
+		}
+
+		if (!cp.matchesBlock(blockState.getBlockId(), k)) {
+			return null;
+		} else {
+			if (side >= 0 && cp.faces != 63) {
+				int l = side;
+
+				if (i != 0) {
+					l = fixSideByAxis(side, i);
+				}
+
+				if ((1 << l & cp.faces) == 0) {
+					return null;
+				}
+			}
+
+			int i1 = blockPos.getY();
+
+			if (cp.heights != null && !cp.heights.isInRange(i1)) {
+				return null;
+			} else {
+				if (cp.biomes != null) {
+					BiomeGenBase biomegenbase = blockAccess.getBiomeGenForCoords(blockPos);
+
+					if (!cp.matchesBiome(biomegenbase)) {
+						return null;
+					}
+				}
+
+				if (cp.nbtName != null) {
+					String s = TileEntityUtils.getTileEntityName(blockAccess, blockPos);
+
+					if (!cp.nbtName.matchesValue(s)) {
+						return null;
+					}
+				}
+
+				EaglerTextureAtlasSprite textureatlassprite = quad.getSprite();
+
+				switch (cp.method) {
+				case 1:
+					return getQuads(getConnectedTextureCtm(cp, blockAccess, blockState, blockPos, i, side,
+							textureatlassprite, j, renderEnv), quad, renderEnv);
+
+				case 2:
+					return getQuads(getConnectedTextureHorizontal(cp, blockAccess, blockState, blockPos, i, side,
+							textureatlassprite, j), quad, renderEnv);
+
+				case 3:
+					return getQuads(getConnectedTextureTop(cp, blockAccess, blockState, blockPos, i, side,
+							textureatlassprite, j), quad, renderEnv);
+
+				case 4:
+					return getQuads(getConnectedTextureRandom(cp, blockAccess, blockState, blockPos, side), quad,
+							renderEnv);
+
+				case 5:
+					return getQuads(getConnectedTextureRepeat(cp, blockPos, side), quad, renderEnv);
+
+				case 6:
+					return getQuads(getConnectedTextureVertical(cp, blockAccess, blockState, blockPos, i, side,
+							textureatlassprite, j), quad, renderEnv);
+
+				case 7:
+					return getQuads(getConnectedTextureFixed(cp), quad, renderEnv);
+
+				case 8:
+					return getQuads(getConnectedTextureHorizontalVertical(cp, blockAccess, blockState, blockPos, i,
+							side, textureatlassprite, j), quad, renderEnv);
+
+				case 9:
+					return getQuads(getConnectedTextureVerticalHorizontal(cp, blockAccess, blockState, blockPos, i,
+							side, textureatlassprite, j), quad, renderEnv);
+
+				case 10:
+					if (pass == 0) {
+						return getConnectedTextureCtmCompact(cp, blockAccess, blockState, blockPos, i, side, quad, j,
+								renderEnv);
+					}
+
+				default:
+					return null;
+
+				case 11:
+					return getConnectedTextureOverlay(cp, blockAccess, blockState, blockPos, i, side, quad, j,
+							renderEnv);
+
+				case 12:
+					return getConnectedTextureOverlayFixed(cp, quad, renderEnv);
+
+				case 13:
+					return getConnectedTextureOverlayRandom(cp, blockAccess, blockState, blockPos, side, quad,
+							renderEnv);
+
+				case 14:
+					return getConnectedTextureOverlayRepeat(cp, blockPos, side, quad, renderEnv);
+
+				case 15:
+					return getConnectedTextureOverlayCtm(cp, blockAccess, blockState, blockPos, i, side, quad, j,
+							renderEnv);
+				}
+			}
+		}
+	}
+
+	private static int fixSideByAxis(int side, int vertAxis) {
+		switch (vertAxis) {
+		case 0:
+			return side;
+
+		case 1:
+			switch (side) {
+			case 0:
+				return 2;
+
+			case 1:
+				return 3;
+
+			case 2:
+				return 1;
+
+			case 3:
+				return 0;
+
+			default:
+				return side;
+			}
+
+		case 2:
+			switch (side) {
+			case 0:
+				return 4;
+
+			case 1:
+				return 5;
+
+			case 2:
+			case 3:
+			default:
+				return side;
+
+			case 4:
+				return 1;
+
+			case 5:
+				return 0;
+			}
+
+		default:
+			return side;
+		}
+	}
+
+	private static int getWoodAxis(int side, int metadata) {
+		int i = (metadata & 12) >> 2;
+
+		switch (i) {
+		case 1:
+			return 2;
+
+		case 2:
+			return 1;
+
+		default:
+			return 0;
+		}
+	}
+
+	private static int getQuartzAxis(int side, int metadata) {
+		switch (metadata) {
+		case 3:
+			return 2;
+
+		case 4:
+			return 1;
+
+		default:
+			return 0;
+		}
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureRandom(ConnectedProperties cp, IBlockAccess blockAccess,
+			BlockStateBase blockState, BlockPos blockPos, int side) {
+		if (cp.tileIcons.length == 1) {
+			return cp.tileIcons[0];
+		} else {
+			int i = side / cp.symmetry * cp.symmetry;
+
+			if (cp.linked) {
+				BlockPos blockpos = blockPos.down();
+
+				for (IBlockState iblockstate = blockAccess.getBlockState(blockpos); iblockstate.getBlock() == blockState
+						.getBlock(); iblockstate = blockAccess.getBlockState(blockpos)) {
+					blockPos = blockpos;
+					blockpos = blockpos.down();
+
+					if (blockpos.getY() < 0) {
+						break;
+					}
+				}
+			}
+
+			int l = Config.getRandom(blockPos, i) & Integer.MAX_VALUE;
+
+			for (int i1 = 0; i1 < cp.randomLoops; ++i1) {
+				l = Config.intHash(l);
+			}
+
+			int j1 = 0;
+
+			if (cp.weights == null) {
+				j1 = l % cp.tileIcons.length;
+			} else {
+				int j = l % cp.sumAllWeights;
+				int[] aint = cp.sumWeights;
+
+				for (int k = 0; k < aint.length; ++k) {
+					if (j < aint[k]) {
+						j1 = k;
+						break;
+					}
+				}
+			}
+
+			return cp.tileIcons[j1];
+		}
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureFixed(ConnectedProperties cp) {
+		return cp.tileIcons[0];
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureRepeat(ConnectedProperties cp, BlockPos blockPos,
+			int side) {
+		if (cp.tileIcons.length == 1) {
+			return cp.tileIcons[0];
+		} else {
+			int i = blockPos.getX();
+			int j = blockPos.getY();
+			int k = blockPos.getZ();
+			int l = 0;
+			int i1 = 0;
+
+			switch (side) {
+			case 0:
+				l = i;
+				i1 = -k - 1;
+				break;
+
+			case 1:
+				l = i;
+				i1 = k;
+				break;
+
+			case 2:
+				l = -i - 1;
+				i1 = -j;
+				break;
+
+			case 3:
+				l = i;
+				i1 = -j;
+				break;
+
+			case 4:
+				l = k;
+				i1 = -j;
+				break;
+
+			case 5:
+				l = -k - 1;
+				i1 = -j;
+			}
+
+			l = l % cp.width;
+			i1 = i1 % cp.height;
+
+			if (l < 0) {
+				l += cp.width;
+			}
+
+			if (i1 < 0) {
+				i1 += cp.height;
+			}
+
+			int j1 = i1 * cp.width + l;
+			return cp.tileIcons[j1];
+		}
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureCtm(ConnectedProperties cp, IBlockAccess blockAccess,
+			IBlockState blockState, BlockPos blockPos, int vertAxis, int side, EaglerTextureAtlasSprite icon,
+			int metadata, RenderEnv renderEnv) {
+		int i = getConnectedTextureCtmIndex(cp, blockAccess, blockState, blockPos, vertAxis, side, icon, metadata,
+				renderEnv);
+		return cp.tileIcons[i];
+	}
+
+	private static synchronized BakedQuad[] getConnectedTextureCtmCompact(ConnectedProperties cp,
+			IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos, int vertAxis, int side, BakedQuad quad,
+			int metadata, RenderEnv renderEnv) {
+		EaglerTextureAtlasSprite textureatlassprite = quad.getSprite();
+		int i = getConnectedTextureCtmIndex(cp, blockAccess, blockState, blockPos, vertAxis, side, textureatlassprite,
+				metadata, renderEnv);
+		return ConnectedTexturesCompact.getConnectedTextureCtmCompact(i, cp, side, quad, renderEnv);
+	}
+
+	private static BakedQuad[] getConnectedTextureOverlay(ConnectedProperties cp, IBlockAccess blockAccess,
+			IBlockState blockState, BlockPos blockPos, int vertAxis, int side, BakedQuad quad, int metadata,
+			RenderEnv renderEnv) {
+		if (!quad.isFullQuad()) {
+			return null;
+		} else {
+			EaglerTextureAtlasSprite textureatlassprite = quad.getSprite();
+			BlockDir[] ablockdir = getSideDirections(side, vertAxis);
+			boolean[] aboolean = renderEnv.getBorderFlags();
+
+			for (int i = 0; i < 4; ++i) {
+				aboolean[i] = isNeighbourOverlay(cp, blockAccess, blockState, ablockdir[i].offset(blockPos), side,
+						textureatlassprite, metadata);
+			}
+
+			ListQuadsOverlay listquadsoverlay = renderEnv.getListQuadsOverlay(cp.layer);
+			Object dirEdges;
+
+			try {
+				if (!aboolean[0] || !aboolean[1] || !aboolean[2] || !aboolean[3]) {
+					if (aboolean[0] && aboolean[1] && aboolean[2]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[5], quad, cp.tintIndex), cp.tintBlockState);
+						dirEdges = null;
+						return (BakedQuad[]) dirEdges;
+					}
+
+					if (aboolean[0] && aboolean[2] && aboolean[3]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[6], quad, cp.tintIndex), cp.tintBlockState);
+						dirEdges = null;
+						return (BakedQuad[]) dirEdges;
+					}
+
+					if (aboolean[1] && aboolean[2] && aboolean[3]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[12], quad, cp.tintIndex), cp.tintBlockState);
+						dirEdges = null;
+						return (BakedQuad[]) dirEdges;
+					}
+
+					if (aboolean[0] && aboolean[1] && aboolean[3]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[13], quad, cp.tintIndex), cp.tintBlockState);
+						dirEdges = null;
+						return (BakedQuad[]) dirEdges;
+					}
+
+					BlockDir[] ablockdir1 = getEdgeDirections(side, vertAxis);
+					boolean[] aboolean1 = renderEnv.getBorderFlags2();
+
+					for (int j = 0; j < 4; ++j) {
+						aboolean1[j] = isNeighbourOverlay(cp, blockAccess, blockState, ablockdir1[j].offset(blockPos),
+								side, textureatlassprite, metadata);
+					}
+
+					if (aboolean[1] && aboolean[2]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[3], quad, cp.tintIndex), cp.tintBlockState);
+
+						if (aboolean1[3]) {
+							listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[16], quad, cp.tintIndex),
+									cp.tintBlockState);
+						}
+
+						Object object4 = null;
+						return (BakedQuad[]) object4;
+					}
+
+					if (aboolean[0] && aboolean[2]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[4], quad, cp.tintIndex), cp.tintBlockState);
+
+						if (aboolean1[2]) {
+							listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[14], quad, cp.tintIndex),
+									cp.tintBlockState);
+						}
+
+						Object object3 = null;
+						return (BakedQuad[]) object3;
+					}
+
+					if (aboolean[1] && aboolean[3]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[10], quad, cp.tintIndex), cp.tintBlockState);
+
+						if (aboolean1[1]) {
+							listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[2], quad, cp.tintIndex),
+									cp.tintBlockState);
+						}
+
+						Object object2 = null;
+						return (BakedQuad[]) object2;
+					}
+
+					if (aboolean[0] && aboolean[3]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[11], quad, cp.tintIndex), cp.tintBlockState);
+
+						if (aboolean1[0]) {
+							listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[0], quad, cp.tintIndex),
+									cp.tintBlockState);
+						}
+
+						Object object1 = null;
+						return (BakedQuad[]) object1;
+					}
+
+					boolean[] aboolean2 = renderEnv.getBorderFlags3();
+
+					for (int k = 0; k < 4; ++k) {
+						aboolean2[k] = isNeighbourMatching(cp, blockAccess, blockState, ablockdir[k].offset(blockPos),
+								side, textureatlassprite, metadata);
+					}
+
+					if (aboolean[0]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[9], quad, cp.tintIndex), cp.tintBlockState);
+					}
+
+					if (aboolean[1]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[7], quad, cp.tintIndex), cp.tintBlockState);
+					}
+
+					if (aboolean[2]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[1], quad, cp.tintIndex), cp.tintBlockState);
+					}
+
+					if (aboolean[3]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[15], quad, cp.tintIndex), cp.tintBlockState);
+					}
+
+					if (aboolean1[0] && (aboolean2[1] || aboolean2[2]) && !aboolean[1] && !aboolean[2]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[0], quad, cp.tintIndex), cp.tintBlockState);
+					}
+
+					if (aboolean1[1] && (aboolean2[0] || aboolean2[2]) && !aboolean[0] && !aboolean[2]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[2], quad, cp.tintIndex), cp.tintBlockState);
+					}
+
+					if (aboolean1[2] && (aboolean2[1] || aboolean2[3]) && !aboolean[1] && !aboolean[3]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[14], quad, cp.tintIndex), cp.tintBlockState);
+					}
+
+					if (aboolean1[3] && (aboolean2[0] || aboolean2[3]) && !aboolean[0] && !aboolean[3]) {
+						listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[16], quad, cp.tintIndex), cp.tintBlockState);
+					}
+
+					Object object5 = null;
+					return (BakedQuad[]) object5;
+				}
+
+				listquadsoverlay.addQuad(getQuadFull(cp.tileIcons[8], quad, cp.tintIndex), cp.tintBlockState);
+				dirEdges = null;
+			} finally {
+				if (listquadsoverlay.size() > 0) {
+					renderEnv.setOverlaysRendered(true);
+				}
+			}
+
+			return (BakedQuad[]) dirEdges;
+		}
+	}
+
+	private static BakedQuad[] getConnectedTextureOverlayFixed(ConnectedProperties cp, BakedQuad quad,
+			RenderEnv renderEnv) {
+		if (!quad.isFullQuad()) {
+			return null;
+		} else {
+			ListQuadsOverlay listquadsoverlay = renderEnv.getListQuadsOverlay(cp.layer);
+			Object object;
+
+			try {
+				EaglerTextureAtlasSprite textureatlassprite = getConnectedTextureFixed(cp);
+
+				if (textureatlassprite != null) {
+					listquadsoverlay.addQuad(getQuadFull(textureatlassprite, quad, cp.tintIndex), cp.tintBlockState);
+				}
+
+				object = null;
+			} finally {
+				if (listquadsoverlay.size() > 0) {
+					renderEnv.setOverlaysRendered(true);
+				}
+			}
+
+			return (BakedQuad[]) object;
+		}
+	}
+
+	private static BakedQuad[] getConnectedTextureOverlayRandom(ConnectedProperties cp, IBlockAccess blockAccess,
+			BlockStateBase blockState, BlockPos blockPos, int side, BakedQuad quad, RenderEnv renderEnv) {
+		if (!quad.isFullQuad()) {
+			return null;
+		} else {
+			ListQuadsOverlay listquadsoverlay = renderEnv.getListQuadsOverlay(cp.layer);
+			Object object;
+
+			try {
+				EaglerTextureAtlasSprite textureatlassprite = getConnectedTextureRandom(cp, blockAccess, blockState,
+						blockPos, side);
+
+				if (textureatlassprite != null) {
+					listquadsoverlay.addQuad(getQuadFull(textureatlassprite, quad, cp.tintIndex), cp.tintBlockState);
+				}
+
+				object = null;
+			} finally {
+				if (listquadsoverlay.size() > 0) {
+					renderEnv.setOverlaysRendered(true);
+				}
+			}
+
+			return (BakedQuad[]) object;
+		}
+	}
+
+	private static BakedQuad[] getConnectedTextureOverlayRepeat(ConnectedProperties cp, BlockPos blockPos, int side,
+			BakedQuad quad, RenderEnv renderEnv) {
+		if (!quad.isFullQuad()) {
+			return null;
+		} else {
+			ListQuadsOverlay listquadsoverlay = renderEnv.getListQuadsOverlay(cp.layer);
+			Object object;
+
+			try {
+				EaglerTextureAtlasSprite textureatlassprite = getConnectedTextureRepeat(cp, blockPos, side);
+
+				if (textureatlassprite != null) {
+					listquadsoverlay.addQuad(getQuadFull(textureatlassprite, quad, cp.tintIndex), cp.tintBlockState);
+				}
+
+				object = null;
+			} finally {
+				if (listquadsoverlay.size() > 0) {
+					renderEnv.setOverlaysRendered(true);
+				}
+			}
+
+			return (BakedQuad[]) object;
+		}
+	}
+
+	private static BakedQuad[] getConnectedTextureOverlayCtm(ConnectedProperties cp, IBlockAccess blockAccess,
+			IBlockState blockState, BlockPos blockPos, int vertAxis, int side, BakedQuad quad, int metadata,
+			RenderEnv renderEnv) {
+		if (!quad.isFullQuad()) {
+			return null;
+		} else {
+			ListQuadsOverlay listquadsoverlay = renderEnv.getListQuadsOverlay(cp.layer);
+			Object object;
+
+			try {
+				EaglerTextureAtlasSprite textureatlassprite = getConnectedTextureCtm(cp, blockAccess, blockState,
+						blockPos, vertAxis, side, quad.getSprite(), metadata, renderEnv);
+
+				if (textureatlassprite != null) {
+					listquadsoverlay.addQuad(getQuadFull(textureatlassprite, quad, cp.tintIndex), cp.tintBlockState);
+				}
+
+				object = null;
+			} finally {
+				if (listquadsoverlay.size() > 0) {
+					renderEnv.setOverlaysRendered(true);
+				}
+			}
+
+			return (BakedQuad[]) object;
+		}
+	}
+
+	private static BlockDir[] getSideDirections(int side, int vertAxis) {
+		switch (side) {
+		case 0:
+			return SIDES_Y_NEG_DOWN;
+
+		case 1:
+			return SIDES_Y_POS_UP;
+
+		case 2:
+			if (vertAxis == 1) {
+				return SIDES_Z_NEG_NORTH_Z_AXIS;
+			}
+
+			return SIDES_Z_NEG_NORTH;
+
+		case 3:
+			return SIDES_Z_POS_SOUTH;
+
+		case 4:
+			return SIDES_X_NEG_WEST;
+
+		case 5:
+			if (vertAxis == 2) {
+				return SIDES_X_POS_EAST_X_AXIS;
+			}
+
+			return SIDES_X_POS_EAST;
+
+		default:
+			throw new IllegalArgumentException("Unknown side: " + side);
+		}
+	}
+
+	private static BlockDir[] getEdgeDirections(int side, int vertAxis) {
+		switch (side) {
+		case 0:
+			return EDGES_Y_NEG_DOWN;
+
+		case 1:
+			return EDGES_Y_POS_UP;
+
+		case 2:
+			if (vertAxis == 1) {
+				return EDGES_Z_NEG_NORTH_Z_AXIS;
+			}
+
+			return EDGES_Z_NEG_NORTH;
+
+		case 3:
+			return EDGES_Z_POS_SOUTH;
+
+		case 4:
+			return EDGES_X_NEG_WEST;
+
+		case 5:
+			if (vertAxis == 2) {
+				return EDGES_X_POS_EAST_X_AXIS;
+			}
+
+			return EDGES_X_POS_EAST;
+
+		default:
+			throw new IllegalArgumentException("Unknown side: " + side);
+		}
+	}
+
+	protected static Map[][] getSpriteQuadCompactMaps() {
+		return spriteQuadCompactMaps;
+	}
+
+	private static int getConnectedTextureCtmIndex(ConnectedProperties cp, IBlockAccess blockAccess,
+			IBlockState blockState, BlockPos blockPos, int vertAxis, int side, EaglerTextureAtlasSprite icon,
+			int metadata, RenderEnv renderEnv) {
+		boolean[] aboolean = renderEnv.getBorderFlags();
+
+		switch (side) {
+		case 0:
+			aboolean[0] = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+			aboolean[1] = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+			aboolean[2] = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+			aboolean[3] = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+
+			if (cp.innerSeams) {
+				BlockPos blockpos6 = blockPos.down();
+				aboolean[0] = aboolean[0]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos6.west(), side, icon, metadata);
+				aboolean[1] = aboolean[1]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos6.east(), side, icon, metadata);
+				aboolean[2] = aboolean[2]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos6.north(), side, icon, metadata);
+				aboolean[3] = aboolean[3]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos6.south(), side, icon, metadata);
+			}
+
+			break;
+
+		case 1:
+			aboolean[0] = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+			aboolean[1] = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+			aboolean[2] = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+			aboolean[3] = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+
+			if (cp.innerSeams) {
+				BlockPos blockpos5 = blockPos.up();
+				aboolean[0] = aboolean[0]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos5.west(), side, icon, metadata);
+				aboolean[1] = aboolean[1]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos5.east(), side, icon, metadata);
+				aboolean[2] = aboolean[2]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos5.south(), side, icon, metadata);
+				aboolean[3] = aboolean[3]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos5.north(), side, icon, metadata);
+			}
+
+			break;
+
+		case 2:
+			aboolean[0] = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+			aboolean[1] = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+			aboolean[2] = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+			aboolean[3] = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+
+			if (cp.innerSeams) {
+				BlockPos blockpos4 = blockPos.north();
+				aboolean[0] = aboolean[0]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos4.east(), side, icon, metadata);
+				aboolean[1] = aboolean[1]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos4.west(), side, icon, metadata);
+				aboolean[2] = aboolean[2]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos4.down(), side, icon, metadata);
+				aboolean[3] = aboolean[3]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos4.up(), side, icon, metadata);
+			}
+
+			if (vertAxis == 1) {
+				switchValues(0, 1, aboolean);
+				switchValues(2, 3, aboolean);
+			}
+
+			break;
+
+		case 3:
+			aboolean[0] = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+			aboolean[1] = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+			aboolean[2] = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+			aboolean[3] = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+
+			if (cp.innerSeams) {
+				BlockPos blockpos3 = blockPos.south();
+				aboolean[0] = aboolean[0]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos3.west(), side, icon, metadata);
+				aboolean[1] = aboolean[1]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos3.east(), side, icon, metadata);
+				aboolean[2] = aboolean[2]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos3.down(), side, icon, metadata);
+				aboolean[3] = aboolean[3]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos3.up(), side, icon, metadata);
+			}
+
+			break;
+
+		case 4:
+			aboolean[0] = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+			aboolean[1] = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+			aboolean[2] = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+			aboolean[3] = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+
+			if (cp.innerSeams) {
+				BlockPos blockpos2 = blockPos.west();
+				aboolean[0] = aboolean[0]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos2.north(), side, icon, metadata);
+				aboolean[1] = aboolean[1]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos2.south(), side, icon, metadata);
+				aboolean[2] = aboolean[2]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos2.down(), side, icon, metadata);
+				aboolean[3] = aboolean[3]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos2.up(), side, icon, metadata);
+			}
+
+			break;
+
+		case 5:
+			aboolean[0] = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+			aboolean[1] = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+			aboolean[2] = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+			aboolean[3] = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+
+			if (cp.innerSeams) {
+				BlockPos blockpos = blockPos.east();
+				aboolean[0] = aboolean[0]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos.south(), side, icon, metadata);
+				aboolean[1] = aboolean[1]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos.north(), side, icon, metadata);
+				aboolean[2] = aboolean[2]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos.down(), side, icon, metadata);
+				aboolean[3] = aboolean[3]
+						&& !isNeighbour(cp, blockAccess, blockState, blockpos.up(), side, icon, metadata);
+			}
+
+			if (vertAxis == 2) {
+				switchValues(0, 1, aboolean);
+				switchValues(2, 3, aboolean);
+			}
+		}
+
+		int i = 0;
+
+		if (aboolean[0] & !aboolean[1] & !aboolean[2] & !aboolean[3]) {
+			i = 3;
+		} else if (!aboolean[0] & aboolean[1] & !aboolean[2] & !aboolean[3]) {
+			i = 1;
+		} else if (!aboolean[0] & !aboolean[1] & aboolean[2] & !aboolean[3]) {
+			i = 12;
+		} else if (!aboolean[0] & !aboolean[1] & !aboolean[2] & aboolean[3]) {
+			i = 36;
+		} else if (aboolean[0] & aboolean[1] & !aboolean[2] & !aboolean[3]) {
+			i = 2;
+		} else if (!aboolean[0] & !aboolean[1] & aboolean[2] & aboolean[3]) {
+			i = 24;
+		} else if (aboolean[0] & !aboolean[1] & aboolean[2] & !aboolean[3]) {
+			i = 15;
+		} else if (aboolean[0] & !aboolean[1] & !aboolean[2] & aboolean[3]) {
+			i = 39;
+		} else if (!aboolean[0] & aboolean[1] & aboolean[2] & !aboolean[3]) {
+			i = 13;
+		} else if (!aboolean[0] & aboolean[1] & !aboolean[2] & aboolean[3]) {
+			i = 37;
+		} else if (!aboolean[0] & aboolean[1] & aboolean[2] & aboolean[3]) {
+			i = 25;
+		} else if (aboolean[0] & !aboolean[1] & aboolean[2] & aboolean[3]) {
+			i = 27;
+		} else if (aboolean[0] & aboolean[1] & !aboolean[2] & aboolean[3]) {
+			i = 38;
+		} else if (aboolean[0] & aboolean[1] & aboolean[2] & !aboolean[3]) {
+			i = 14;
+		} else if (aboolean[0] & aboolean[1] & aboolean[2] & aboolean[3]) {
+			i = 26;
+		}
+
+		if (i == 0) {
+			return i;
+		} else if (!Config.isConnectedTexturesFancy()) {
+			return i;
+		} else {
+			switch (side) {
+			case 0:
+				aboolean[0] = !isNeighbour(cp, blockAccess, blockState, blockPos.east().north(), side, icon, metadata);
+				aboolean[1] = !isNeighbour(cp, blockAccess, blockState, blockPos.west().north(), side, icon, metadata);
+				aboolean[2] = !isNeighbour(cp, blockAccess, blockState, blockPos.east().south(), side, icon, metadata);
+				aboolean[3] = !isNeighbour(cp, blockAccess, blockState, blockPos.west().south(), side, icon, metadata);
+
+				if (cp.innerSeams) {
+					BlockPos blockpos11 = blockPos.down();
+					aboolean[0] = aboolean[0] || isNeighbour(cp, blockAccess, blockState, blockpos11.east().north(),
+							side, icon, metadata);
+					aboolean[1] = aboolean[1] || isNeighbour(cp, blockAccess, blockState, blockpos11.west().north(),
+							side, icon, metadata);
+					aboolean[2] = aboolean[2] || isNeighbour(cp, blockAccess, blockState, blockpos11.east().south(),
+							side, icon, metadata);
+					aboolean[3] = aboolean[3] || isNeighbour(cp, blockAccess, blockState, blockpos11.west().south(),
+							side, icon, metadata);
+				}
+
+				break;
+
+			case 1:
+				aboolean[0] = !isNeighbour(cp, blockAccess, blockState, blockPos.east().south(), side, icon, metadata);
+				aboolean[1] = !isNeighbour(cp, blockAccess, blockState, blockPos.west().south(), side, icon, metadata);
+				aboolean[2] = !isNeighbour(cp, blockAccess, blockState, blockPos.east().north(), side, icon, metadata);
+				aboolean[3] = !isNeighbour(cp, blockAccess, blockState, blockPos.west().north(), side, icon, metadata);
+
+				if (cp.innerSeams) {
+					BlockPos blockpos10 = blockPos.up();
+					aboolean[0] = aboolean[0] || isNeighbour(cp, blockAccess, blockState, blockpos10.east().south(),
+							side, icon, metadata);
+					aboolean[1] = aboolean[1] || isNeighbour(cp, blockAccess, blockState, blockpos10.west().south(),
+							side, icon, metadata);
+					aboolean[2] = aboolean[2] || isNeighbour(cp, blockAccess, blockState, blockpos10.east().north(),
+							side, icon, metadata);
+					aboolean[3] = aboolean[3] || isNeighbour(cp, blockAccess, blockState, blockpos10.west().north(),
+							side, icon, metadata);
+				}
+
+				break;
+
+			case 2:
+				aboolean[0] = !isNeighbour(cp, blockAccess, blockState, blockPos.west().down(), side, icon, metadata);
+				aboolean[1] = !isNeighbour(cp, blockAccess, blockState, blockPos.east().down(), side, icon, metadata);
+				aboolean[2] = !isNeighbour(cp, blockAccess, blockState, blockPos.west().up(), side, icon, metadata);
+				aboolean[3] = !isNeighbour(cp, blockAccess, blockState, blockPos.east().up(), side, icon, metadata);
+
+				if (cp.innerSeams) {
+					BlockPos blockpos9 = blockPos.north();
+					aboolean[0] = aboolean[0]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos9.west().down(), side, icon, metadata);
+					aboolean[1] = aboolean[1]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos9.east().down(), side, icon, metadata);
+					aboolean[2] = aboolean[2]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos9.west().up(), side, icon, metadata);
+					aboolean[3] = aboolean[3]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos9.east().up(), side, icon, metadata);
+				}
+
+				if (vertAxis == 1) {
+					switchValues(0, 3, aboolean);
+					switchValues(1, 2, aboolean);
+				}
+
+				break;
+
+			case 3:
+				aboolean[0] = !isNeighbour(cp, blockAccess, blockState, blockPos.east().down(), side, icon, metadata);
+				aboolean[1] = !isNeighbour(cp, blockAccess, blockState, blockPos.west().down(), side, icon, metadata);
+				aboolean[2] = !isNeighbour(cp, blockAccess, blockState, blockPos.east().up(), side, icon, metadata);
+				aboolean[3] = !isNeighbour(cp, blockAccess, blockState, blockPos.west().up(), side, icon, metadata);
+
+				if (cp.innerSeams) {
+					BlockPos blockpos8 = blockPos.south();
+					aboolean[0] = aboolean[0]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos8.east().down(), side, icon, metadata);
+					aboolean[1] = aboolean[1]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos8.west().down(), side, icon, metadata);
+					aboolean[2] = aboolean[2]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos8.east().up(), side, icon, metadata);
+					aboolean[3] = aboolean[3]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos8.west().up(), side, icon, metadata);
+				}
+
+				break;
+
+			case 4:
+				aboolean[0] = !isNeighbour(cp, blockAccess, blockState, blockPos.down().south(), side, icon, metadata);
+				aboolean[1] = !isNeighbour(cp, blockAccess, blockState, blockPos.down().north(), side, icon, metadata);
+				aboolean[2] = !isNeighbour(cp, blockAccess, blockState, blockPos.up().south(), side, icon, metadata);
+				aboolean[3] = !isNeighbour(cp, blockAccess, blockState, blockPos.up().north(), side, icon, metadata);
+
+				if (cp.innerSeams) {
+					BlockPos blockpos7 = blockPos.west();
+					aboolean[0] = aboolean[0]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos7.down().south(), side, icon, metadata);
+					aboolean[1] = aboolean[1]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos7.down().north(), side, icon, metadata);
+					aboolean[2] = aboolean[2]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos7.up().south(), side, icon, metadata);
+					aboolean[3] = aboolean[3]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos7.up().north(), side, icon, metadata);
+				}
+
+				break;
+
+			case 5:
+				aboolean[0] = !isNeighbour(cp, blockAccess, blockState, blockPos.down().north(), side, icon, metadata);
+				aboolean[1] = !isNeighbour(cp, blockAccess, blockState, blockPos.down().south(), side, icon, metadata);
+				aboolean[2] = !isNeighbour(cp, blockAccess, blockState, blockPos.up().north(), side, icon, metadata);
+				aboolean[3] = !isNeighbour(cp, blockAccess, blockState, blockPos.up().south(), side, icon, metadata);
+
+				if (cp.innerSeams) {
+					BlockPos blockpos1 = blockPos.east();
+					aboolean[0] = aboolean[0]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos1.down().north(), side, icon, metadata);
+					aboolean[1] = aboolean[1]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos1.down().south(), side, icon, metadata);
+					aboolean[2] = aboolean[2]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos1.up().north(), side, icon, metadata);
+					aboolean[3] = aboolean[3]
+							|| isNeighbour(cp, blockAccess, blockState, blockpos1.up().south(), side, icon, metadata);
+				}
+
+				if (vertAxis == 2) {
+					switchValues(0, 3, aboolean);
+					switchValues(1, 2, aboolean);
+				}
+			}
+
+			if (i == 13 && aboolean[0]) {
+				i = 4;
+			} else if (i == 15 && aboolean[1]) {
+				i = 5;
+			} else if (i == 37 && aboolean[2]) {
+				i = 16;
+			} else if (i == 39 && aboolean[3]) {
+				i = 17;
+			} else if (i == 14 && aboolean[0] && aboolean[1]) {
+				i = 7;
+			} else if (i == 25 && aboolean[0] && aboolean[2]) {
+				i = 6;
+			} else if (i == 27 && aboolean[3] && aboolean[1]) {
+				i = 19;
+			} else if (i == 38 && aboolean[3] && aboolean[2]) {
+				i = 18;
+			} else if (i == 14 && !aboolean[0] && aboolean[1]) {
+				i = 31;
+			} else if (i == 25 && aboolean[0] && !aboolean[2]) {
+				i = 30;
+			} else if (i == 27 && !aboolean[3] && aboolean[1]) {
+				i = 41;
+			} else if (i == 38 && aboolean[3] && !aboolean[2]) {
+				i = 40;
+			} else if (i == 14 && aboolean[0] && !aboolean[1]) {
+				i = 29;
+			} else if (i == 25 && !aboolean[0] && aboolean[2]) {
+				i = 28;
+			} else if (i == 27 && aboolean[3] && !aboolean[1]) {
+				i = 43;
+			} else if (i == 38 && !aboolean[3] && aboolean[2]) {
+				i = 42;
+			} else if (i == 26 && aboolean[0] && aboolean[1] && aboolean[2] && aboolean[3]) {
+				i = 46;
+			} else if (i == 26 && !aboolean[0] && aboolean[1] && aboolean[2] && aboolean[3]) {
+				i = 9;
+			} else if (i == 26 && aboolean[0] && !aboolean[1] && aboolean[2] && aboolean[3]) {
+				i = 21;
+			} else if (i == 26 && aboolean[0] && aboolean[1] && !aboolean[2] && aboolean[3]) {
+				i = 8;
+			} else if (i == 26 && aboolean[0] && aboolean[1] && aboolean[2] && !aboolean[3]) {
+				i = 20;
+			} else if (i == 26 && aboolean[0] && aboolean[1] && !aboolean[2] && !aboolean[3]) {
+				i = 11;
+			} else if (i == 26 && !aboolean[0] && !aboolean[1] && aboolean[2] && aboolean[3]) {
+				i = 22;
+			} else if (i == 26 && !aboolean[0] && aboolean[1] && !aboolean[2] && aboolean[3]) {
+				i = 23;
+			} else if (i == 26 && aboolean[0] && !aboolean[1] && aboolean[2] && !aboolean[3]) {
+				i = 10;
+			} else if (i == 26 && aboolean[0] && !aboolean[1] && !aboolean[2] && aboolean[3]) {
+				i = 34;
+			} else if (i == 26 && !aboolean[0] && aboolean[1] && aboolean[2] && !aboolean[3]) {
+				i = 35;
+			} else if (i == 26 && aboolean[0] && !aboolean[1] && !aboolean[2] && !aboolean[3]) {
+				i = 32;
+			} else if (i == 26 && !aboolean[0] && aboolean[1] && !aboolean[2] && !aboolean[3]) {
+				i = 33;
+			} else if (i == 26 && !aboolean[0] && !aboolean[1] && aboolean[2] && !aboolean[3]) {
+				i = 44;
+			} else if (i == 26 && !aboolean[0] && !aboolean[1] && !aboolean[2] && aboolean[3]) {
+				i = 45;
+			}
+
+			return i;
+		}
+	}
+
+	private static void switchValues(int ix1, int ix2, boolean[] arr) {
+		boolean flag = arr[ix1];
+		arr[ix1] = arr[ix2];
+		arr[ix2] = flag;
+	}
+
+	private static boolean isNeighbourOverlay(ConnectedProperties cp, IBlockAccess iblockaccess, IBlockState blockState,
+			BlockPos blockPos, int side, EaglerTextureAtlasSprite icon, int metadata) {
+		IBlockState iblockstate = iblockaccess.getBlockState(blockPos);
+
+		if (!isFullCubeModel(iblockstate)) {
+			return false;
+		} else {
+			if (cp.connectBlocks != null) {
+				BlockStateBase blockstatebase = (BlockStateBase) iblockstate;
+
+				if (!Matches.block(blockstatebase.getBlockId(), blockstatebase.getMetadata(), cp.connectBlocks)) {
+					return false;
+				}
+			}
+
+			if (cp.connectTileIcons != null) {
+				EaglerTextureAtlasSprite textureatlassprite = getNeighbourIcon(iblockaccess, blockState, blockPos,
+						iblockstate, side);
+
+				if (!Config.isSameOne(textureatlassprite, cp.connectTileIcons)) {
+					return false;
+				}
+			}
+
+			IBlockState iblockstate1 = iblockaccess.getBlockState(blockPos.offset(getFacing(side)));
+			return iblockstate1.getBlock().isOpaqueCube() ? false
+					: (side == 1 && iblockstate1.getBlock() == Blocks.snow_layer ? false
+							: !isNeighbour(cp, iblockaccess, blockState, blockPos, iblockstate, side, icon, metadata));
+		}
+	}
+
+	private static boolean isFullCubeModel(IBlockState state) {
+		if (state.getBlock().isFullCube()) {
+			return true;
+		} else {
+			Block block = state.getBlock();
+			return block instanceof BlockGlass ? true : block instanceof BlockStainedGlass;
+		}
+	}
+
+	private static boolean isNeighbourMatching(ConnectedProperties cp, IBlockAccess iblockaccess,
+			IBlockState blockState, BlockPos blockPos, int side, EaglerTextureAtlasSprite icon, int metadata) {
+		IBlockState iblockstate = iblockaccess.getBlockState(blockPos);
+
+		if (iblockstate == AIR_DEFAULT_STATE) {
+			return false;
+		} else {
+			if (cp.matchBlocks != null && iblockstate instanceof BlockStateBase) {
+				BlockStateBase blockstatebase = (BlockStateBase) iblockstate;
+
+				if (!cp.matchesBlock(blockstatebase.getBlockId(), blockstatebase.getMetadata())) {
+					return false;
+				}
+			}
+
+			if (cp.matchTileIcons != null) {
+				EaglerTextureAtlasSprite textureatlassprite = getNeighbourIcon(iblockaccess, blockState, blockPos,
+						iblockstate, side);
+
+				if (textureatlassprite != icon) {
+					return false;
+				}
+			}
+
+			IBlockState iblockstate1 = iblockaccess.getBlockState(blockPos.offset(getFacing(side)));
+			return iblockstate1.getBlock().isOpaqueCube() ? false
+					: side != 1 || iblockstate1.getBlock() != Blocks.snow_layer;
+		}
+	}
+
+	private static boolean isNeighbour(ConnectedProperties cp, IBlockAccess iblockaccess, IBlockState blockState,
+			BlockPos blockPos, int side, EaglerTextureAtlasSprite icon, int metadata) {
+		IBlockState iblockstate = iblockaccess.getBlockState(blockPos);
+		return isNeighbour(cp, iblockaccess, blockState, blockPos, iblockstate, side, icon, metadata);
+	}
+
+	private static boolean isNeighbour(ConnectedProperties cp, IBlockAccess iblockaccess, IBlockState blockState,
+			BlockPos blockPos, IBlockState neighbourState, int side, EaglerTextureAtlasSprite icon, int metadata) {
+		if (blockState == neighbourState) {
+			return true;
+		} else if (cp.connect == 2) {
+			if (neighbourState == null) {
+				return false;
+			} else if (neighbourState == AIR_DEFAULT_STATE) {
+				return false;
+			} else {
+				EaglerTextureAtlasSprite textureatlassprite = getNeighbourIcon(iblockaccess, blockState, blockPos,
+						neighbourState, side);
+				return textureatlassprite == icon;
+			}
+		} else if (cp.connect == 3) {
+			return neighbourState == null ? false
+					: (neighbourState == AIR_DEFAULT_STATE ? false
+							: neighbourState.getBlock().getMaterial() == blockState.getBlock().getMaterial());
+		} else if (!(neighbourState instanceof BlockStateBase)) {
+			return false;
+		} else {
+			BlockStateBase blockstatebase = (BlockStateBase) neighbourState;
+			Block block = blockstatebase.getBlock();
+			int i = blockstatebase.getMetadata();
+			return block == blockState.getBlock() && i == metadata;
+		}
+	}
+
+	private static EaglerTextureAtlasSprite getNeighbourIcon(IBlockAccess iblockaccess, IBlockState blockState,
+			BlockPos blockPos, IBlockState neighbourState, int side) {
+		neighbourState = neighbourState.getBlock().getActualState(neighbourState, iblockaccess, blockPos);
+		IBakedModel ibakedmodel = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes()
+				.getModelForState(neighbourState);
+
+		if (ibakedmodel == null) {
+			return null;
+		} else {
+//			if (Reflector.ForgeBlock_getExtendedState.exists()) {
+//				neighbourState = (IBlockState) Reflector.call(neighbourState.getBlock(),
+//						Reflector.ForgeBlock_getExtendedState, new Object[] { neighbourState, iblockaccess, blockPos });
+//			}
+
+			EnumFacing enumfacing = getFacing(side);
+			List list = ibakedmodel.getFaceQuads(enumfacing);
+
+			if (list == null) {
+				return null;
+			} else {
+				if (Config.isBetterGrass()) {
+					list = BetterGrass.getFaceQuads(iblockaccess, neighbourState, blockPos, enumfacing, list);
+				}
+
+				if (list.size() > 0) {
+					BakedQuad bakedquad1 = (BakedQuad) list.get(0);
+					return bakedquad1.getSprite();
+				} else {
+					List list1 = ibakedmodel.getGeneralQuads();
+
+					if (list1 == null) {
+						return null;
+					} else {
+						for (int i = 0; i < list1.size(); ++i) {
+							BakedQuad bakedquad = (BakedQuad) list1.get(i);
+
+							if (bakedquad.getFace() == enumfacing) {
+								return bakedquad.getSprite();
+							}
+						}
+
+						return null;
+					}
+				}
+			}
+		}
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureHorizontal(ConnectedProperties cp,
+			IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos, int vertAxis, int side,
+			EaglerTextureAtlasSprite icon, int metadata) {
+		boolean flag;
+		boolean flag1;
+		flag = false;
+		flag1 = false;
+		label0:
+
+		switch (vertAxis) {
+		case 0:
+			switch (side) {
+			case 0:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+				break label0;
+
+			case 1:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+				break label0;
+
+			case 2:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				break label0;
+
+			case 3:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+				break label0;
+
+			case 4:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+				break label0;
+
+			case 5:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+
+			default:
+				break label0;
+			}
+
+		case 1:
+			switch (side) {
+			case 0:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				break label0;
+
+			case 1:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+				break label0;
+
+			case 2:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+				break label0;
+
+			case 3:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+				break label0;
+
+			case 4:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+				break label0;
+
+			case 5:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+
+			default:
+				break label0;
+			}
+
+		case 2:
+			switch (side) {
+			case 0:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+				break;
+
+			case 1:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+				break;
+
+			case 2:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+				break;
+
+			case 3:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+				break;
+
+			case 4:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+				break;
+
+			case 5:
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+			}
+		}
+
+		int i = 3;
+
+		if (flag) {
+			if (flag1) {
+				i = 1;
+			} else {
+				i = 2;
+			}
+		} else if (flag1) {
+			i = 0;
+		} else {
+			i = 3;
+		}
+
+		return cp.tileIcons[i];
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureVertical(ConnectedProperties cp,
+			IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos, int vertAxis, int side,
+			EaglerTextureAtlasSprite icon, int metadata) {
+		boolean flag = false;
+		boolean flag1 = false;
+
+		switch (vertAxis) {
+		case 0:
+			if (side == 1) {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+			} else if (side == 0) {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+			} else {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+			}
+
+			break;
+
+		case 1:
+			if (side == 3) {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+			} else if (side == 2) {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+			} else {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.north(), side, icon, metadata);
+			}
+
+			break;
+
+		case 2:
+			if (side == 5) {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+			} else if (side == 4) {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.down(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+			} else {
+				flag = isNeighbour(cp, blockAccess, blockState, blockPos.west(), side, icon, metadata);
+				flag1 = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+			}
+		}
+
+		int i = 3;
+
+		if (flag) {
+			if (flag1) {
+				i = 1;
+			} else {
+				i = 2;
+			}
+		} else if (flag1) {
+			i = 0;
+		} else {
+			i = 3;
+		}
+
+		return cp.tileIcons[i];
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureHorizontalVertical(ConnectedProperties cp,
+			IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos, int vertAxis, int side,
+			EaglerTextureAtlasSprite icon, int metadata) {
+		EaglerTextureAtlasSprite[] atextureatlassprite = cp.tileIcons;
+		EaglerTextureAtlasSprite textureatlassprite = getConnectedTextureHorizontal(cp, blockAccess, blockState,
+				blockPos, vertAxis, side, icon, metadata);
+
+		if (textureatlassprite != null && textureatlassprite != icon && textureatlassprite != atextureatlassprite[3]) {
+			return textureatlassprite;
+		} else {
+			EaglerTextureAtlasSprite textureatlassprite1 = getConnectedTextureVertical(cp, blockAccess, blockState,
+					blockPos, vertAxis, side, icon, metadata);
+			return textureatlassprite1 == atextureatlassprite[0] ? atextureatlassprite[4]
+					: (textureatlassprite1 == atextureatlassprite[1] ? atextureatlassprite[5]
+							: (textureatlassprite1 == atextureatlassprite[2] ? atextureatlassprite[6]
+									: textureatlassprite1));
+		}
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureVerticalHorizontal(ConnectedProperties cp,
+			IBlockAccess blockAccess, IBlockState blockState, BlockPos blockPos, int vertAxis, int side,
+			EaglerTextureAtlasSprite icon, int metadata) {
+		EaglerTextureAtlasSprite[] atextureatlassprite = cp.tileIcons;
+		EaglerTextureAtlasSprite textureatlassprite = getConnectedTextureVertical(cp, blockAccess, blockState, blockPos,
+				vertAxis, side, icon, metadata);
+
+		if (textureatlassprite != null && textureatlassprite != icon && textureatlassprite != atextureatlassprite[3]) {
+			return textureatlassprite;
+		} else {
+			EaglerTextureAtlasSprite textureatlassprite1 = getConnectedTextureHorizontal(cp, blockAccess, blockState,
+					blockPos, vertAxis, side, icon, metadata);
+			return textureatlassprite1 == atextureatlassprite[0] ? atextureatlassprite[4]
+					: (textureatlassprite1 == atextureatlassprite[1] ? atextureatlassprite[5]
+							: (textureatlassprite1 == atextureatlassprite[2] ? atextureatlassprite[6]
+									: textureatlassprite1));
+		}
+	}
+
+	private static EaglerTextureAtlasSprite getConnectedTextureTop(ConnectedProperties cp, IBlockAccess blockAccess,
+			IBlockState blockState, BlockPos blockPos, int vertAxis, int side, EaglerTextureAtlasSprite icon,
+			int metadata) {
+		boolean flag = false;
+
+		switch (vertAxis) {
+		case 0:
+			if (side == 1 || side == 0) {
+				return null;
+			}
+
+			flag = isNeighbour(cp, blockAccess, blockState, blockPos.up(), side, icon, metadata);
+			break;
+
+		case 1:
+			if (side == 3 || side == 2) {
+				return null;
+			}
+
+			flag = isNeighbour(cp, blockAccess, blockState, blockPos.south(), side, icon, metadata);
+			break;
+
+		case 2:
+			if (side == 5 || side == 4) {
+				return null;
+			}
+
+			flag = isNeighbour(cp, blockAccess, blockState, blockPos.east(), side, icon, metadata);
+		}
+
+		if (flag) {
+			return cp.tileIcons[0];
+		} else {
+			return null;
+		}
+	}
+
+	public static void updateIcons(TextureMap textureMap) {
+		blockProperties = (ConnectedProperties[][]) null;
+		tileProperties = (ConnectedProperties[][]) null;
+		spriteQuadMaps = null;
+		spriteQuadCompactMaps = (Map[][]) null;
+
+		if (Config.isConnectedTextures()) {
+			IResourcePack[] airesourcepack = Config.getResourcePacks();
+
+			for (int i = airesourcepack.length - 1; i >= 0; --i) {
+				IResourcePack iresourcepack = airesourcepack[i];
+				updateIcons(textureMap, iresourcepack);
+			}
+
+			updateIcons(textureMap, Minecraft.getMinecraft().getDefaultResourcePack());
+			ResourceLocation resourcelocation = new ResourceLocation("mcpatcher/ctm/default/empty");
+			emptySprite = textureMap.registerSprite(resourcelocation);
+			spriteQuadMaps = new Map[textureMap.getCountRegisteredSprites() + 1];
+			spriteQuadFullMaps = new Map[textureMap.getCountRegisteredSprites() + 1];
+			spriteQuadCompactMaps = new Map[textureMap.getCountRegisteredSprites() + 1][];
+
+			if (blockProperties.length <= 0) {
+				blockProperties = (ConnectedProperties[][]) null;
+			}
+
+			if (tileProperties.length <= 0) {
+				tileProperties = (ConnectedProperties[][]) null;
+			}
+		}
+	}
+
+	private static void updateIconEmpty(TextureMap textureMap) {
+	}
+
+	public static void updateIcons(TextureMap textureMap, IResourcePack rp) {
+		List<String> astring = ResUtils.collectPropertyFiles(rp, "mcpatcher/ctm/");
+		Collections.sort(astring);
+		List list = makePropertyList(tileProperties);
+		List list1 = makePropertyList(blockProperties);
+
+		for (int i = 0, l = astring.size(); i < l; ++i) {
+			String s = astring.get(i);
+			Config.dbg("ConnectedTextures: " + s);
+
+			try {
+				ResourceLocation resourcelocation = new ResourceLocation(s);
+				InputStream inputstream = rp.getInputStream(resourcelocation);
+
+				if (inputstream == null) {
+					Config.warn("ConnectedTextures file not found: " + s);
+				} else {
+					Properties properties = new PropertiesOrdered();
+					properties.load(inputstream);
+					inputstream.close();
+					ConnectedProperties connectedproperties = new ConnectedProperties(properties, s);
+
+					if (connectedproperties.isValid(s)) {
+						connectedproperties.updateIcons(textureMap);
+						addToTileList(connectedproperties, list);
+						addToBlockList(connectedproperties, list1);
+					}
+				}
+			} catch (FileNotFoundException var11) {
+				Config.warn("ConnectedTextures file not found: " + s);
+			} catch (Exception exception) {
+				exception.printStackTrace();
+			}
+		}
+
+		blockProperties = propertyListToArray(list1);
+		tileProperties = propertyListToArray(list);
+		multipass = detectMultipass();
+		Config.dbg("Multipass connected textures: " + multipass);
+	}
+
+	private static List makePropertyList(ConnectedProperties[][] propsArr) {
+		List list = new ArrayList();
+
+		if (propsArr != null) {
+			for (int i = 0; i < propsArr.length; ++i) {
+				ConnectedProperties[] aconnectedproperties = propsArr[i];
+				List list1 = null;
+
+				if (aconnectedproperties != null) {
+					list1 = new ArrayList(Arrays.asList(aconnectedproperties));
+				}
+
+				list.add(list1);
+			}
+		}
+
+		return list;
+	}
+
+	private static boolean detectMultipass() {
+		List list = new ArrayList();
+
+		for (int i = 0; i < tileProperties.length; ++i) {
+			ConnectedProperties[] aconnectedproperties = tileProperties[i];
+
+			if (aconnectedproperties != null) {
+				list.addAll(Arrays.asList(aconnectedproperties));
+			}
+		}
+
+		for (int k = 0; k < blockProperties.length; ++k) {
+			ConnectedProperties[] aconnectedproperties2 = blockProperties[k];
+
+			if (aconnectedproperties2 != null) {
+				list.addAll(Arrays.asList(aconnectedproperties2));
+			}
+		}
+
+		ConnectedProperties[] aconnectedproperties1 = (ConnectedProperties[]) ((ConnectedProperties[]) list
+				.toArray(new ConnectedProperties[list.size()]));
+		Set set1 = new HashSet();
+		Set set = new HashSet();
+
+		for (int j = 0; j < aconnectedproperties1.length; ++j) {
+			ConnectedProperties connectedproperties = aconnectedproperties1[j];
+
+			if (connectedproperties.matchTileIcons != null) {
+				set1.addAll(Arrays.asList(connectedproperties.matchTileIcons));
+			}
+
+			if (connectedproperties.tileIcons != null) {
+				set.addAll(Arrays.asList(connectedproperties.tileIcons));
+			}
+		}
+
+		set1.retainAll(set);
+		return !set1.isEmpty();
+	}
+
+	private static ConnectedProperties[][] propertyListToArray(List list) {
+		ConnectedProperties[][] aconnectedproperties = new ConnectedProperties[list.size()][];
+
+		for (int i = 0; i < list.size(); ++i) {
+			List list2 = (List) list.get(i);
+
+			if (list2 != null) {
+				ConnectedProperties[] aconnectedproperties1 = (ConnectedProperties[]) list2
+						.toArray(new ConnectedProperties[list2.size()]);
+				aconnectedproperties[i] = aconnectedproperties1;
+			}
+		}
+
+		return aconnectedproperties;
+	}
+
+	private static void addToTileList(ConnectedProperties cp, List tileList) {
+		if (cp.matchTileIcons != null) {
+			for (int i = 0; i < cp.matchTileIcons.length; ++i) {
+				EaglerTextureAtlasSprite textureatlassprite = cp.matchTileIcons[i];
+				int j = textureatlassprite.getIndexInMap();
+
+				if (j < 0) {
+					Config.warn("Invalid tile ID: " + j + ", icon: " + textureatlassprite.getIconName());
+				} else {
+					addToList(cp, tileList, j);
+				}
+			}
+		}
+	}
+
+	private static void addToBlockList(ConnectedProperties cp, List blockList) {
+		if (cp.matchBlocks != null) {
+			for (int i = 0; i < cp.matchBlocks.length; ++i) {
+				int j = cp.matchBlocks[i].getBlockId();
+
+				if (j < 0) {
+					Config.warn("Invalid block ID: " + j);
+				} else {
+					addToList(cp, blockList, j);
+				}
+			}
+		}
+	}
+
+	private static void addToList(ConnectedProperties cp, List list, int id) {
+		while (id >= list.size()) {
+			list.add(null);
+		}
+
+		List list1 = (List) list.get(id);
+
+		if (list1 == null) {
+			list1 = new ArrayList();
+			list.set(id, list1);
+		}
+
+		list1.add(cp);
+	}
+
+}
diff --git a/sources/main/java/net/optifine/ConnectedTexturesCompact.java b/sources/main/java/net/optifine/ConnectedTexturesCompact.java
new file mode 100644
index 00000000..7fb491ee
--- /dev/null
+++ b/sources/main/java/net/optifine/ConnectedTexturesCompact.java
@@ -0,0 +1,450 @@
+package net.optifine;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.optifine.render.RenderEnv;
+
+public class ConnectedTexturesCompact {
+	private static final int COMPACT_NONE = 0;
+	private static final int COMPACT_ALL = 1;
+	private static final int COMPACT_V = 2;
+	private static final int COMPACT_H = 3;
+	private static final int COMPACT_HV = 4;
+
+	public static BakedQuad[] getConnectedTextureCtmCompact(int ctmIndex, ConnectedProperties cp, int side,
+			BakedQuad quad, RenderEnv renderEnv) {
+		if (cp.ctmTileIndexes != null && ctmIndex >= 0 && ctmIndex < cp.ctmTileIndexes.length) {
+			int i = cp.ctmTileIndexes[ctmIndex];
+
+			if (i >= 0 && i <= cp.tileIcons.length) {
+				return getQuadsCompact(i, cp.tileIcons, quad, renderEnv);
+			}
+		}
+
+		switch (ctmIndex) {
+		case 1:
+			return getQuadsCompactH(0, 3, cp.tileIcons, side, quad, renderEnv);
+
+		case 2:
+			return getQuadsCompact(3, cp.tileIcons, quad, renderEnv);
+
+		case 3:
+			return getQuadsCompactH(3, 0, cp.tileIcons, side, quad, renderEnv);
+
+		case 4:
+			return getQuadsCompact4(0, 3, 2, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 5:
+			return getQuadsCompact4(3, 0, 4, 2, cp.tileIcons, side, quad, renderEnv);
+
+		case 6:
+			return getQuadsCompact4(2, 4, 2, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 7:
+			return getQuadsCompact4(3, 3, 4, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 8:
+			return getQuadsCompact4(4, 1, 4, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 9:
+			return getQuadsCompact4(4, 4, 4, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 10:
+			return getQuadsCompact4(1, 4, 1, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 11:
+			return getQuadsCompact4(1, 1, 4, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 12:
+			return getQuadsCompactV(0, 2, cp.tileIcons, side, quad, renderEnv);
+
+		case 13:
+			return getQuadsCompact4(0, 3, 2, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 14:
+			return getQuadsCompactV(3, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 15:
+			return getQuadsCompact4(3, 0, 1, 2, cp.tileIcons, side, quad, renderEnv);
+
+		case 16:
+			return getQuadsCompact4(2, 4, 0, 3, cp.tileIcons, side, quad, renderEnv);
+
+		case 17:
+			return getQuadsCompact4(4, 2, 3, 0, cp.tileIcons, side, quad, renderEnv);
+
+		case 18:
+			return getQuadsCompact4(4, 4, 3, 3, cp.tileIcons, side, quad, renderEnv);
+
+		case 19:
+			return getQuadsCompact4(4, 2, 4, 2, cp.tileIcons, side, quad, renderEnv);
+
+		case 20:
+			return getQuadsCompact4(1, 4, 4, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 21:
+			return getQuadsCompact4(4, 4, 1, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 22:
+			return getQuadsCompact4(4, 4, 1, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 23:
+			return getQuadsCompact4(4, 1, 4, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 24:
+			return getQuadsCompact(2, cp.tileIcons, quad, renderEnv);
+
+		case 25:
+			return getQuadsCompactH(2, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 26:
+			return getQuadsCompact(1, cp.tileIcons, quad, renderEnv);
+
+		case 27:
+			return getQuadsCompactH(1, 2, cp.tileIcons, side, quad, renderEnv);
+
+		case 28:
+			return getQuadsCompact4(2, 4, 2, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 29:
+			return getQuadsCompact4(3, 3, 1, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 30:
+			return getQuadsCompact4(2, 1, 2, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 31:
+			return getQuadsCompact4(3, 3, 4, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 32:
+			return getQuadsCompact4(1, 1, 1, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 33:
+			return getQuadsCompact4(1, 1, 4, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 34:
+			return getQuadsCompact4(4, 1, 1, 4, cp.tileIcons, side, quad, renderEnv);
+
+		case 35:
+			return getQuadsCompact4(1, 4, 4, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 36:
+			return getQuadsCompactV(2, 0, cp.tileIcons, side, quad, renderEnv);
+
+		case 37:
+			return getQuadsCompact4(2, 1, 0, 3, cp.tileIcons, side, quad, renderEnv);
+
+		case 38:
+			return getQuadsCompactV(1, 3, cp.tileIcons, side, quad, renderEnv);
+
+		case 39:
+			return getQuadsCompact4(1, 2, 3, 0, cp.tileIcons, side, quad, renderEnv);
+
+		case 40:
+			return getQuadsCompact4(4, 1, 3, 3, cp.tileIcons, side, quad, renderEnv);
+
+		case 41:
+			return getQuadsCompact4(1, 2, 4, 2, cp.tileIcons, side, quad, renderEnv);
+
+		case 42:
+			return getQuadsCompact4(1, 4, 3, 3, cp.tileIcons, side, quad, renderEnv);
+
+		case 43:
+			return getQuadsCompact4(4, 2, 1, 2, cp.tileIcons, side, quad, renderEnv);
+
+		case 44:
+			return getQuadsCompact4(1, 4, 1, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 45:
+			return getQuadsCompact4(4, 1, 1, 1, cp.tileIcons, side, quad, renderEnv);
+
+		case 46:
+			return getQuadsCompact(4, cp.tileIcons, quad, renderEnv);
+
+		default:
+			return getQuadsCompact(0, cp.tileIcons, quad, renderEnv);
+		}
+	}
+
+	private static BakedQuad[] getQuadsCompactH(int indexLeft, int indexRight, EaglerTextureAtlasSprite[] sprites,
+			int side, BakedQuad quad, RenderEnv renderEnv) {
+		return getQuadsCompact(ConnectedTexturesCompact.Dir.LEFT, indexLeft, ConnectedTexturesCompact.Dir.RIGHT,
+				indexRight, sprites, side, quad, renderEnv);
+	}
+
+	private static BakedQuad[] getQuadsCompactV(int indexUp, int indexDown, EaglerTextureAtlasSprite[] sprites,
+			int side, BakedQuad quad, RenderEnv renderEnv) {
+		return getQuadsCompact(ConnectedTexturesCompact.Dir.UP, indexUp, ConnectedTexturesCompact.Dir.DOWN, indexDown,
+				sprites, side, quad, renderEnv);
+	}
+
+	private static BakedQuad[] getQuadsCompact4(int upLeft, int upRight, int downLeft, int downRight,
+			EaglerTextureAtlasSprite[] sprites, int side, BakedQuad quad, RenderEnv renderEnv) {
+		return upLeft == upRight ? (downLeft == downRight
+				? getQuadsCompact(ConnectedTexturesCompact.Dir.UP, upLeft, ConnectedTexturesCompact.Dir.DOWN, downLeft,
+						sprites, side, quad, renderEnv)
+				: getQuadsCompact(ConnectedTexturesCompact.Dir.UP, upLeft, ConnectedTexturesCompact.Dir.DOWN_LEFT,
+						downLeft, ConnectedTexturesCompact.Dir.DOWN_RIGHT, downRight, sprites, side, quad, renderEnv))
+				: (downLeft == downRight
+						? getQuadsCompact(ConnectedTexturesCompact.Dir.UP_LEFT, upLeft,
+								ConnectedTexturesCompact.Dir.UP_RIGHT, upRight, ConnectedTexturesCompact.Dir.DOWN,
+								downLeft, sprites, side, quad, renderEnv)
+						: (upLeft == downLeft
+								? (upRight == downRight ? getQuadsCompact(ConnectedTexturesCompact.Dir.LEFT, upLeft,
+										ConnectedTexturesCompact.Dir.RIGHT, upRight, sprites, side, quad, renderEnv)
+										: getQuadsCompact(ConnectedTexturesCompact.Dir.LEFT, upLeft,
+												ConnectedTexturesCompact.Dir.UP_RIGHT, upRight,
+												ConnectedTexturesCompact.Dir.DOWN_RIGHT, downRight, sprites, side, quad,
+												renderEnv))
+								: (upRight == downRight ? getQuadsCompact(ConnectedTexturesCompact.Dir.UP_LEFT, upLeft,
+										ConnectedTexturesCompact.Dir.DOWN_LEFT, downLeft,
+										ConnectedTexturesCompact.Dir.RIGHT, upRight, sprites, side, quad, renderEnv)
+										: getQuadsCompact(ConnectedTexturesCompact.Dir.UP_LEFT, upLeft,
+												ConnectedTexturesCompact.Dir.UP_RIGHT, upRight,
+												ConnectedTexturesCompact.Dir.DOWN_LEFT, downLeft,
+												ConnectedTexturesCompact.Dir.DOWN_RIGHT, downRight, sprites, side, quad,
+												renderEnv))));
+	}
+
+	private static BakedQuad[] getQuadsCompact(int index, EaglerTextureAtlasSprite[] sprites, BakedQuad quad,
+			RenderEnv renderEnv) {
+		EaglerTextureAtlasSprite textureatlassprite = sprites[index];
+		return ConnectedTextures.getQuads(textureatlassprite, quad, renderEnv);
+	}
+
+	private static BakedQuad[] getQuadsCompact(ConnectedTexturesCompact.Dir dir1, int index1,
+			ConnectedTexturesCompact.Dir dir2, int index2, EaglerTextureAtlasSprite[] sprites, int side, BakedQuad quad,
+			RenderEnv renderEnv) {
+		BakedQuad bakedquad = getQuadCompact(sprites[index1], dir1, side, quad, renderEnv);
+		BakedQuad bakedquad1 = getQuadCompact(sprites[index2], dir2, side, quad, renderEnv);
+		return renderEnv.getArrayQuadsCtm(bakedquad, bakedquad1);
+	}
+
+	private static BakedQuad[] getQuadsCompact(ConnectedTexturesCompact.Dir dir1, int index1,
+			ConnectedTexturesCompact.Dir dir2, int index2, ConnectedTexturesCompact.Dir dir3, int index3,
+			EaglerTextureAtlasSprite[] sprites, int side, BakedQuad quad, RenderEnv renderEnv) {
+		BakedQuad bakedquad = getQuadCompact(sprites[index1], dir1, side, quad, renderEnv);
+		BakedQuad bakedquad1 = getQuadCompact(sprites[index2], dir2, side, quad, renderEnv);
+		BakedQuad bakedquad2 = getQuadCompact(sprites[index3], dir3, side, quad, renderEnv);
+		return renderEnv.getArrayQuadsCtm(bakedquad, bakedquad1, bakedquad2);
+	}
+
+	private static BakedQuad[] getQuadsCompact(ConnectedTexturesCompact.Dir dir1, int index1,
+			ConnectedTexturesCompact.Dir dir2, int index2, ConnectedTexturesCompact.Dir dir3, int index3,
+			ConnectedTexturesCompact.Dir dir4, int index4, EaglerTextureAtlasSprite[] sprites, int side, BakedQuad quad,
+			RenderEnv renderEnv) {
+		BakedQuad bakedquad = getQuadCompact(sprites[index1], dir1, side, quad, renderEnv);
+		BakedQuad bakedquad1 = getQuadCompact(sprites[index2], dir2, side, quad, renderEnv);
+		BakedQuad bakedquad2 = getQuadCompact(sprites[index3], dir3, side, quad, renderEnv);
+		BakedQuad bakedquad3 = getQuadCompact(sprites[index4], dir4, side, quad, renderEnv);
+		return renderEnv.getArrayQuadsCtm(bakedquad, bakedquad1, bakedquad2, bakedquad3);
+	}
+
+	private static BakedQuad getQuadCompact(EaglerTextureAtlasSprite sprite, ConnectedTexturesCompact.Dir dir, int side,
+			BakedQuad quad, RenderEnv renderEnv) {
+		switch (dir) {
+		case UP:
+			return getQuadCompact(sprite, dir, 0, 0, 16, 8, side, quad, renderEnv);
+
+		case UP_RIGHT:
+			return getQuadCompact(sprite, dir, 8, 0, 16, 8, side, quad, renderEnv);
+
+		case RIGHT:
+			return getQuadCompact(sprite, dir, 8, 0, 16, 16, side, quad, renderEnv);
+
+		case DOWN_RIGHT:
+			return getQuadCompact(sprite, dir, 8, 8, 16, 16, side, quad, renderEnv);
+
+		case DOWN:
+			return getQuadCompact(sprite, dir, 0, 8, 16, 16, side, quad, renderEnv);
+
+		case DOWN_LEFT:
+			return getQuadCompact(sprite, dir, 0, 8, 8, 16, side, quad, renderEnv);
+
+		case LEFT:
+			return getQuadCompact(sprite, dir, 0, 0, 8, 16, side, quad, renderEnv);
+
+		case UP_LEFT:
+			return getQuadCompact(sprite, dir, 0, 0, 8, 8, side, quad, renderEnv);
+
+		default:
+			return quad;
+		}
+	}
+
+	private static BakedQuad getQuadCompact(EaglerTextureAtlasSprite sprite, ConnectedTexturesCompact.Dir dir, int x1,
+			int y1, int x2, int y2, int side, BakedQuad quadIn, RenderEnv renderEnv) {
+		Map[][] amap = ConnectedTextures.getSpriteQuadCompactMaps();
+
+		if (amap == null) {
+			return quadIn;
+		} else {
+			int i = sprite.getIndexInMap();
+
+			if (i >= 0 && i < amap.length) {
+				Map[] amap1 = amap[i];
+
+				if (amap1 == null) {
+					amap1 = new Map[ConnectedTexturesCompact.Dir.VALUES.length];
+					amap[i] = amap1;
+				}
+
+				Map<BakedQuad, BakedQuad> map = amap1[dir.ordinal()];
+
+				if (map == null) {
+					map = new IdentityHashMap(1);
+					amap1[dir.ordinal()] = map;
+				}
+
+				BakedQuad bakedquad = (BakedQuad) map.get(quadIn);
+
+				if (bakedquad == null) {
+					bakedquad = makeSpriteQuadCompact(quadIn, sprite, side, x1, y1, x2, y2);
+					map.put(quadIn, bakedquad);
+				}
+
+				return bakedquad;
+			} else {
+				return quadIn;
+			}
+		}
+	}
+
+	private static BakedQuad makeSpriteQuadCompact(BakedQuad quad, EaglerTextureAtlasSprite sprite, int side, int x1,
+			int y1, int x2, int y2) {
+		int[] aint = (int[]) quad.getVertexData().clone();
+		int[] aint2 = (int[]) quad.getVertexDataWithNormals().clone();
+		EaglerTextureAtlasSprite textureatlassprite = quad.getSprite();
+
+		for (int i = 0; i < 4; ++i) {
+			fixVertexCompact(aint, aint2, i, textureatlassprite, sprite, side, x1, y1, x2, y2);
+		}
+
+		BakedQuad bakedquad = new BakedQuad(aint, aint2, quad.getTintIndex(), quad.getFace(), sprite);
+		return bakedquad;
+	}
+
+	private static void fixVertexCompact(int[] data, int[] data2, int vertex, EaglerTextureAtlasSprite spriteFrom,
+			EaglerTextureAtlasSprite spriteTo, int side, int x1, int y1, int x2, int y2) {
+		int i = data.length / 4;
+		int j = i * vertex;
+		int i2 = data2.length / 4;
+		int j2 = i2 * vertex;
+		float f = Float.intBitsToFloat(data[j + 4]);
+		float f1 = Float.intBitsToFloat(data[j + 4 + 1]);
+		double d0 = spriteFrom.getSpriteU16(f);
+		double d1 = spriteFrom.getSpriteV16(f1);
+		float f2 = Float.intBitsToFloat(data[j + 0]);
+		float f3 = Float.intBitsToFloat(data[j + 1]);
+		float f4 = Float.intBitsToFloat(data[j + 2]);
+		float f5;
+		float f6;
+
+		switch (side) {
+		case 0:
+			f5 = f2;
+			f6 = 1.0F - f4;
+			break;
+
+		case 1:
+			f5 = f2;
+			f6 = f4;
+			break;
+
+		case 2:
+			f5 = 1.0F - f2;
+			f6 = 1.0F - f3;
+			break;
+
+		case 3:
+			f5 = f2;
+			f6 = 1.0F - f3;
+			break;
+
+		case 4:
+			f5 = f4;
+			f6 = 1.0F - f3;
+			break;
+
+		case 5:
+			f5 = 1.0F - f4;
+			f6 = 1.0F - f3;
+			break;
+
+		default:
+			return;
+		}
+
+		float f7 = 15.968F;
+		float f8 = 15.968F;
+
+		if (d0 < (double) x1) {
+			f5 = (float) ((double) f5 + ((double) x1 - d0) / (double) f7);
+			d0 = (double) x1;
+		}
+
+		if (d0 > (double) x2) {
+			f5 = (float) ((double) f5 - (d0 - (double) x2) / (double) f7);
+			d0 = (double) x2;
+		}
+
+		if (d1 < (double) y1) {
+			f6 = (float) ((double) f6 + ((double) y1 - d1) / (double) f8);
+			d1 = (double) y1;
+		}
+
+		if (d1 > (double) y2) {
+			f6 = (float) ((double) f6 - (d1 - (double) y2) / (double) f8);
+			d1 = (double) y2;
+		}
+
+		switch (side) {
+		case 0:
+			f2 = f5;
+			f4 = 1.0F - f6;
+			break;
+
+		case 1:
+			f2 = f5;
+			f4 = f6;
+			break;
+
+		case 2:
+			f2 = 1.0F - f5;
+			f3 = 1.0F - f6;
+			break;
+
+		case 3:
+			f2 = f5;
+			f3 = 1.0F - f6;
+			break;
+
+		case 4:
+			f4 = f5;
+			f3 = 1.0F - f6;
+			break;
+
+		case 5:
+			f4 = 1.0F - f5;
+			f3 = 1.0F - f6;
+			break;
+
+		default:
+			return;
+		}
+
+		data[j + 4] = data2[j2 + 4] = Float.floatToRawIntBits(spriteTo.getInterpolatedU(d0));
+		data[j + 4 + 1] = data2[j2 + 4 + 1] = Float.floatToRawIntBits(spriteTo.getInterpolatedV(d1));
+		data[j + 0] = data2[j2 + 0] = Float.floatToRawIntBits(f2);
+		data[j + 1] = data2[j2 + 1] = Float.floatToRawIntBits(f3);
+		data[j + 2] = data2[j2 + 2] = Float.floatToRawIntBits(f4);
+	}
+
+	private static enum Dir {
+		UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT;
+
+		public static final ConnectedTexturesCompact.Dir[] VALUES = values();
+	}
+}
diff --git a/sources/main/java/net/optifine/CustomItemProperties.java b/sources/main/java/net/optifine/CustomItemProperties.java
new file mode 100644
index 00000000..c368b81d
--- /dev/null
+++ b/sources/main/java/net/optifine/CustomItemProperties.java
@@ -0,0 +1,972 @@
+package net.optifine;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL;
+import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
+import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
+import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.renderer.block.model.BlockPart;
+import net.minecraft.client.renderer.block.model.BlockPartFace;
+import net.minecraft.client.renderer.block.model.FaceBakery;
+import net.minecraft.client.renderer.block.model.ItemModelGenerator;
+import net.minecraft.client.renderer.block.model.ModelBlock;
+import net.minecraft.client.renderer.texture.ITextureObject;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.client.resources.model.ModelBakery;
+import net.minecraft.client.resources.model.ModelManager;
+import net.minecraft.client.resources.model.ModelResourceLocation;
+import net.minecraft.client.resources.model.ModelRotation;
+import net.minecraft.client.resources.model.SimpleBakedModel;
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemArmor;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.optifine.config.IParserInt;
+import net.optifine.config.NbtTagValue;
+import net.optifine.config.ParserEnchantmentId;
+import net.optifine.config.RangeInt;
+import net.optifine.config.RangeListInt;
+import net.optifine.render.Blender;
+import net.optifine.util.StrUtils;
+import net.optifine.util.TextureUtils;
+
+public class CustomItemProperties {
+	public String name = null;
+	public String basePath = null;
+	public int type = 1;
+	public int[] items = null;
+	public String texture = null;
+	public Map<String, String> mapTextures = null;
+	public String model = null;
+	public Map<String, String> mapModels = null;
+	public RangeListInt damage = null;
+	public boolean damagePercent = false;
+	public int damageMask = 0;
+	public RangeListInt stackSize = null;
+	public RangeListInt enchantmentIds = null;
+	public RangeListInt enchantmentLevels = null;
+	public NbtTagValue[] nbtTagValues = null;
+	public int hand = 0;
+	public int blend = 1;
+	public float speed = 0.0F;
+	public float rotation = 0.0F;
+	public int layer = 0;
+	public float duration = 1.0F;
+	public int weight = 0;
+	public ResourceLocation textureLocation = null;
+	public Map mapTextureLocations = null;
+	public EaglerTextureAtlasSprite sprite = null;
+	public Map mapSprites = null;
+	public IBakedModel bakedModelTexture = null;
+	public Map<String, IBakedModel> mapBakedModelsTexture = null;
+	public IBakedModel bakedModelFull = null;
+	public Map<String, IBakedModel> mapBakedModelsFull = null;
+	private int textureWidth = 0;
+	private int textureHeight = 0;
+	public static final int TYPE_UNKNOWN = 0;
+	public static final int TYPE_ITEM = 1;
+	public static final int TYPE_ENCHANTMENT = 2;
+	public static final int TYPE_ARMOR = 3;
+	public static final int HAND_ANY = 0;
+	public static final int HAND_MAIN = 1;
+	public static final int HAND_OFF = 2;
+	public static final String INVENTORY = "inventory";
+
+	public CustomItemProperties(Properties props, String path) {
+		this.name = parseName(path);
+		this.basePath = parseBasePath(path);
+		this.type = this.parseType(props.getProperty("type"));
+		this.items = this.parseItems(props.getProperty("items"), props.getProperty("matchItems"));
+		this.mapModels = parseModels(props, this.basePath);
+		this.model = parseModel(props.getProperty("model"), path, this.basePath, this.type, this.mapModels);
+		this.mapTextures = parseTextures(props, this.basePath);
+		boolean flag = this.mapModels == null && this.model == null;
+		this.texture = parseTexture(props.getProperty("texture"), props.getProperty("tile"),
+				props.getProperty("source"), path, this.basePath, this.type, this.mapTextures, flag);
+		String s = props.getProperty("damage");
+
+		if (s != null) {
+			this.damagePercent = s.contains("%");
+			s = s.replace("%", "");
+			this.damage = this.parseRangeListInt(s);
+			this.damageMask = this.parseInt(props.getProperty("damageMask"), 0);
+		}
+
+		this.stackSize = this.parseRangeListInt(props.getProperty("stackSize"));
+		this.enchantmentIds = this.parseRangeListInt(props.getProperty("enchantmentIDs"), new ParserEnchantmentId());
+		this.enchantmentLevels = this.parseRangeListInt(props.getProperty("enchantmentLevels"));
+		this.nbtTagValues = this.parseNbtTagValues(props);
+		this.hand = this.parseHand(props.getProperty("hand"));
+		this.blend = Blender.parseBlend(props.getProperty("blend"));
+		this.speed = this.parseFloat(props.getProperty("speed"), 0.0F);
+		this.rotation = this.parseFloat(props.getProperty("rotation"), 0.0F);
+		this.layer = this.parseInt(props.getProperty("layer"), 0);
+		this.weight = this.parseInt(props.getProperty("weight"), 0);
+		this.duration = this.parseFloat(props.getProperty("duration"), 1.0F);
+	}
+
+	private static String parseName(String path) {
+		String s = path;
+		int i = path.lastIndexOf(47);
+
+		if (i >= 0) {
+			s = path.substring(i + 1);
+		}
+
+		int j = s.lastIndexOf(46);
+
+		if (j >= 0) {
+			s = s.substring(0, j);
+		}
+
+		return s;
+	}
+
+	private static String parseBasePath(String path) {
+		int i = path.lastIndexOf(47);
+		return i < 0 ? "" : path.substring(0, i);
+	}
+
+	private int parseType(String str) {
+		if (str == null) {
+			return 1;
+		} else if (str.equals("item")) {
+			return 1;
+		} else if (str.equals("enchantment")) {
+			return 2;
+		} else if (str.equals("armor")) {
+			return 3;
+		} else {
+			Config.warn("Unknown method: " + str);
+			return 0;
+		}
+	}
+
+	private int[] parseItems(String str, String str2) {
+		if (str == null) {
+			str = str2;
+		}
+
+		if (str == null) {
+			return null;
+		} else {
+			str = str.trim();
+			Set set = new TreeSet();
+			String[] astring = Config.tokenize(str, " ");
+			label45:
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+				int j = Config.parseInt(s, -1);
+
+				if (j >= 0) {
+					set.add(Integer.valueOf(j));
+				} else {
+					if (s.contains("-")) {
+						String[] astring1 = Config.tokenize(s, "-");
+
+						if (astring1.length == 2) {
+							int k = Config.parseInt(astring1[0], -1);
+							int l = Config.parseInt(astring1[1], -1);
+
+							if (k >= 0 && l >= 0) {
+								int i1 = Math.min(k, l);
+								int j1 = Math.max(k, l);
+								int k1 = i1;
+
+								while (true) {
+									if (k1 > j1) {
+										continue label45;
+									}
+
+									set.add(Integer.valueOf(k1));
+									++k1;
+								}
+							}
+						}
+					}
+
+					Item item = Item.getByNameOrId(s);
+
+					if (item == null) {
+						Config.warn("Item not found: " + s);
+					} else {
+						int i2 = Item.getIdFromItem(item);
+
+						if (i2 <= 0) {
+							Config.warn("Item not found: " + s);
+						} else {
+							set.add(Integer.valueOf(i2));
+						}
+					}
+				}
+			}
+
+			Integer[] ainteger = (Integer[]) ((Integer[]) set.toArray(new Integer[set.size()]));
+			int[] aint = new int[ainteger.length];
+
+			for (int l1 = 0; l1 < aint.length; ++l1) {
+				aint[l1] = ainteger[l1].intValue();
+			}
+
+			return aint;
+		}
+	}
+
+	private static String parseTexture(String texStr, String texStr2, String texStr3, String path, String basePath,
+			int type, Map<String, String> mapTexs, boolean textureFromPath) {
+		if (texStr == null) {
+			texStr = texStr2;
+		}
+
+		if (texStr == null) {
+			texStr = texStr3;
+		}
+
+		if (texStr != null) {
+			String s2 = ".png";
+
+			if (texStr.endsWith(s2)) {
+				texStr = texStr.substring(0, texStr.length() - s2.length());
+			}
+
+			texStr = fixTextureName(texStr, basePath);
+			return texStr;
+		} else if (type == 3) {
+			return null;
+		} else {
+			if (mapTexs != null) {
+				String s = (String) mapTexs.get("texture.bow_standby");
+
+				if (s != null) {
+					return s;
+				}
+			}
+
+			if (!textureFromPath) {
+				return null;
+			} else {
+				String s1 = path;
+				int i = path.lastIndexOf(47);
+
+				if (i >= 0) {
+					s1 = path.substring(i + 1);
+				}
+
+				int j = s1.lastIndexOf(46);
+
+				if (j >= 0) {
+					s1 = s1.substring(0, j);
+				}
+
+				s1 = fixTextureName(s1, basePath);
+				return s1;
+			}
+		}
+	}
+
+	private static Map parseTextures(Properties props, String basePath) {
+		String s = "texture.";
+		Map map = getMatchingProperties(props, s);
+
+		if (map.size() <= 0) {
+			return null;
+		} else {
+			Set set = map.keySet();
+			Map map1 = new LinkedHashMap();
+
+			for (Object e : set) {
+				String s1 = (String) e;
+				String s2 = (String) map.get(s1);
+				s2 = fixTextureName(s2, basePath);
+				map1.put(s1, s2);
+			}
+
+			return map1;
+		}
+	}
+
+	private static String fixTextureName(String iconName, String basePath) {
+		iconName = TextureUtils.fixResourcePath(iconName, basePath);
+
+		if (!iconName.startsWith(basePath) && !iconName.startsWith("textures/") && !iconName.startsWith("mcpatcher/")) {
+			iconName = basePath + "/" + iconName;
+		}
+
+		if (iconName.endsWith(".png")) {
+			iconName = iconName.substring(0, iconName.length() - 4);
+		}
+
+		if (iconName.startsWith("/")) {
+			iconName = iconName.substring(1);
+		}
+
+		return iconName;
+	}
+
+	private static String parseModel(String modelStr, String path, String basePath, int type,
+			Map<String, String> mapModelNames) {
+		if (modelStr != null) {
+			String s1 = ".json";
+
+			if (modelStr.endsWith(s1)) {
+				modelStr = modelStr.substring(0, modelStr.length() - s1.length());
+			}
+
+			modelStr = fixModelName(modelStr, basePath);
+			return modelStr;
+		} else if (type == 3) {
+			return null;
+		} else {
+			if (mapModelNames != null) {
+				String s = (String) mapModelNames.get("model.bow_standby");
+
+				if (s != null) {
+					return s;
+				}
+			}
+
+			return modelStr;
+		}
+	}
+
+	private static Map parseModels(Properties props, String basePath) {
+		String s = "model.";
+		Map map = getMatchingProperties(props, s);
+
+		if (map.size() <= 0) {
+			return null;
+		} else {
+			Set set = map.keySet();
+			Map map1 = new LinkedHashMap();
+
+			for (Object e : set) {
+				String s1 = (String) e;
+				String s2 = (String) map.get(s1);
+				s2 = fixModelName(s2, basePath);
+				map1.put(s1, s2);
+			}
+
+			return map1;
+		}
+	}
+
+	private static String fixModelName(String modelName, String basePath) {
+		modelName = TextureUtils.fixResourcePath(modelName, basePath);
+		boolean flag = modelName.startsWith("block/") || modelName.startsWith("item/");
+
+		if (!modelName.startsWith(basePath) && !flag && !modelName.startsWith("mcpatcher/")) {
+			modelName = basePath + "/" + modelName;
+		}
+
+		String s = ".json";
+
+		if (modelName.endsWith(s)) {
+			modelName = modelName.substring(0, modelName.length() - s.length());
+		}
+
+		if (modelName.startsWith("/")) {
+			modelName = modelName.substring(1);
+		}
+
+		return modelName;
+	}
+
+	private int parseInt(String str, int defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			str = str.trim();
+			int i = Config.parseInt(str, Integer.MIN_VALUE);
+
+			if (i == Integer.MIN_VALUE) {
+				Config.warn("Invalid integer: " + str);
+				return defVal;
+			} else {
+				return i;
+			}
+		}
+	}
+
+	private float parseFloat(String str, float defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			str = str.trim();
+			float f = Config.parseFloat(str, Float.MIN_VALUE);
+
+			if (f == Float.MIN_VALUE) {
+				Config.warn("Invalid float: " + str);
+				return defVal;
+			} else {
+				return f;
+			}
+		}
+	}
+
+	private RangeListInt parseRangeListInt(String str) {
+		return this.parseRangeListInt(str, (IParserInt) null);
+	}
+
+	private RangeListInt parseRangeListInt(String str, IParserInt parser) {
+		if (str == null) {
+			return null;
+		} else {
+			String[] astring = Config.tokenize(str, " ");
+			RangeListInt rangelistint = new RangeListInt();
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+
+				if (parser != null) {
+					int j = parser.parse(s, Integer.MIN_VALUE);
+
+					if (j != Integer.MIN_VALUE) {
+						rangelistint.addRange(new RangeInt(j, j));
+						continue;
+					}
+				}
+
+				RangeInt rangeint = this.parseRangeInt(s);
+
+				if (rangeint == null) {
+					Config.warn("Invalid range list: " + str);
+					return null;
+				}
+
+				rangelistint.addRange(rangeint);
+			}
+
+			return rangelistint;
+		}
+	}
+
+	private RangeInt parseRangeInt(String str) {
+		if (str == null) {
+			return null;
+		} else {
+			str = str.trim();
+			int i = str.length() - str.replace("-", "").length();
+
+			if (i > 1) {
+				Config.warn("Invalid range: " + str);
+				return null;
+			} else {
+				String[] astring = Config.tokenize(str, "- ");
+				int[] aint = new int[astring.length];
+
+				for (int j = 0; j < astring.length; ++j) {
+					String s = astring[j];
+					int k = Config.parseInt(s, -1);
+
+					if (k < 0) {
+						Config.warn("Invalid range: " + str);
+						return null;
+					}
+
+					aint[j] = k;
+				}
+
+				if (aint.length == 1) {
+					int i1 = aint[0];
+
+					if (str.startsWith("-")) {
+						return new RangeInt(0, i1);
+					} else if (str.endsWith("-")) {
+						return new RangeInt(i1, 65535);
+					} else {
+						return new RangeInt(i1, i1);
+					}
+				} else if (aint.length == 2) {
+					int l = Math.min(aint[0], aint[1]);
+					int j1 = Math.max(aint[0], aint[1]);
+					return new RangeInt(l, j1);
+				} else {
+					Config.warn("Invalid range: " + str);
+					return null;
+				}
+			}
+		}
+	}
+
+	private NbtTagValue[] parseNbtTagValues(Properties props) {
+		String s = "nbt.";
+		Map map = getMatchingProperties(props, s);
+
+		if (map.size() <= 0) {
+			return null;
+		} else {
+			List list = new ArrayList();
+
+			for (Object e : map.keySet()) {
+				String s1 = (String) e;
+				String s2 = (String) map.get(s1);
+				String s3 = s1.substring(s.length());
+				NbtTagValue nbttagvalue = new NbtTagValue(s3, s2);
+				list.add(nbttagvalue);
+			}
+
+			NbtTagValue[] anbttagvalue = (NbtTagValue[]) ((NbtTagValue[]) list.toArray(new NbtTagValue[list.size()]));
+			return anbttagvalue;
+		}
+	}
+
+	private static Map getMatchingProperties(Properties props, String keyPrefix) {
+		Map map = new LinkedHashMap();
+
+		for (Object e : props.keySet()) {
+			String s = (String) e;
+			String s1 = props.getProperty(s);
+
+			if (s.startsWith(keyPrefix)) {
+				map.put(s, s1);
+			}
+		}
+
+		return map;
+	}
+
+	private int parseHand(String str) {
+		if (str == null) {
+			return 0;
+		} else {
+			str = str.toLowerCase();
+
+			if (str.equals("any")) {
+				return 0;
+			} else if (str.equals("main")) {
+				return 1;
+			} else if (str.equals("off")) {
+				return 2;
+			} else {
+				Config.warn("Invalid hand: " + str);
+				return 0;
+			}
+		}
+	}
+
+	public boolean isValid(String path) {
+		if (this.name != null && this.name.length() > 0) {
+			if (this.basePath == null) {
+				Config.warn("No base path found: " + path);
+				return false;
+			} else if (this.type == 0) {
+				Config.warn("No type defined: " + path);
+				return false;
+			} else {
+				if (this.type == 1 || this.type == 3) {
+					if (this.items == null) {
+						this.items = this.detectItems();
+					}
+
+					if (this.items == null) {
+						Config.warn("No items defined: " + path);
+						return false;
+					}
+				}
+
+				if (this.texture == null && this.mapTextures == null && this.model == null && this.mapModels == null) {
+					Config.warn("No texture or model specified: " + path);
+					return false;
+				} else if (this.type == 2 && this.enchantmentIds == null) {
+					Config.warn("No enchantmentIDs specified: " + path);
+					return false;
+				} else {
+					return true;
+				}
+			}
+		} else {
+			Config.warn("No name found: " + path);
+			return false;
+		}
+	}
+
+	private int[] detectItems() {
+		Item item = Item.getByNameOrId(this.name);
+
+		if (item == null) {
+			return null;
+		} else {
+			int i = Item.getIdFromItem(item);
+			return i <= 0 ? null : new int[] { i };
+		}
+	}
+
+	public void updateIcons(TextureMap textureMap) {
+		if (this.texture != null) {
+			this.textureLocation = this.getTextureLocation(this.texture);
+
+			if (this.type == 1) {
+				ResourceLocation resourcelocation = this.getSpriteLocation(this.textureLocation);
+				this.sprite = textureMap.registerSprite(resourcelocation);
+			}
+		}
+
+		if (this.mapTextures != null) {
+			this.mapTextureLocations = new HashMap();
+			this.mapSprites = new HashMap();
+
+			for (String s : this.mapTextures.keySet()) {
+				String s1 = (String) this.mapTextures.get(s);
+				ResourceLocation resourcelocation1 = this.getTextureLocation(s1);
+				this.mapTextureLocations.put(s, resourcelocation1);
+
+				if (this.type == 1) {
+					ResourceLocation resourcelocation2 = this.getSpriteLocation(resourcelocation1);
+					EaglerTextureAtlasSprite textureatlassprite = textureMap.registerSprite(resourcelocation2);
+					this.mapSprites.put(s, textureatlassprite);
+				}
+			}
+		}
+	}
+
+	private ResourceLocation getTextureLocation(String texName) {
+		if (texName == null) {
+			return null;
+		} else {
+			ResourceLocation resourcelocation = new ResourceLocation(texName);
+			String s = resourcelocation.getResourceDomain();
+			String s1 = resourcelocation.getResourcePath();
+
+			if (!s1.contains("/")) {
+				s1 = "textures/items/" + s1;
+			}
+
+			String s2 = s1 + ".png";
+			ResourceLocation resourcelocation1 = new ResourceLocation(s, s2);
+			boolean flag = Config.hasResource(resourcelocation1);
+
+			if (!flag) {
+				Config.warn("File not found: " + s2);
+			}
+
+			return resourcelocation1;
+		}
+	}
+
+	private ResourceLocation getSpriteLocation(ResourceLocation resLoc) {
+		String s = resLoc.getResourcePath();
+		s = StrUtils.removePrefix(s, "textures/");
+		s = StrUtils.removeSuffix(s, ".png");
+		ResourceLocation resourcelocation = new ResourceLocation(resLoc.getResourceDomain(), s);
+		return resourcelocation;
+	}
+
+	public void updateModelTexture(TextureMap textureMap, ItemModelGenerator itemModelGenerator) {
+		if (this.texture != null || this.mapTextures != null) {
+			String[] astring = this.getModelTextures();
+			boolean flag = this.isUseTint();
+			this.bakedModelTexture = makeBakedModel(textureMap, itemModelGenerator, astring, flag);
+
+			if (this.type == 1 && this.mapTextures != null) {
+				for (String s : this.mapTextures.keySet()) {
+					String s1 = (String) this.mapTextures.get(s);
+					String s2 = StrUtils.removePrefix(s, "texture.");
+
+					if (s2.startsWith("bow") || s2.startsWith("fishing_rod") || s2.startsWith("shield")) {
+						String[] astring1 = new String[] { s1 };
+						IBakedModel ibakedmodel = makeBakedModel(textureMap, itemModelGenerator, astring1, flag);
+
+						if (this.mapBakedModelsTexture == null) {
+							this.mapBakedModelsTexture = new HashMap();
+						}
+
+						this.mapBakedModelsTexture.put(s2, ibakedmodel);
+					}
+				}
+			}
+		}
+	}
+
+	private boolean isUseTint() {
+		return true;
+	}
+
+	private static IBakedModel makeBakedModel(TextureMap textureMap, ItemModelGenerator itemModelGenerator,
+			String[] textures, boolean useTint) {
+		String[] astring = new String[textures.length];
+
+		for (int i = 0; i < astring.length; ++i) {
+			String s = textures[i];
+			astring[i] = StrUtils.removePrefix(s, "textures/");
+		}
+
+		ModelBlock modelblock = makeModelBlock(astring);
+		ModelBlock modelblock1 = itemModelGenerator.makeItemModel(textureMap, modelblock);
+		IBakedModel ibakedmodel = bakeModel(textureMap, modelblock1, useTint);
+		return ibakedmodel;
+	}
+
+	private String[] getModelTextures() {
+		if (this.type == 1 && this.items.length == 1) {
+			Item item = Item.getItemById(this.items[0]);
+
+			if (item == Items.potionitem && this.damage != null && this.damage.getCountRanges() > 0) {
+				RangeInt rangeint = this.damage.getRange(0);
+				int i = rangeint.getMin();
+				boolean flag = (i & 16384) != 0;
+				String s5 = this.getMapTexture(this.mapTextures, "texture.potion_overlay", "items/potion_overlay");
+				String s6 = null;
+
+				if (flag) {
+					s6 = this.getMapTexture(this.mapTextures, "texture.potion_bottle_splash",
+							"items/potion_bottle_splash");
+				} else {
+					s6 = this.getMapTexture(this.mapTextures, "texture.potion_bottle_drinkable",
+							"items/potion_bottle_drinkable");
+				}
+
+				return new String[] { s5, s6 };
+			}
+
+			if (item instanceof ItemArmor) {
+				ItemArmor itemarmor = (ItemArmor) item;
+
+				if (itemarmor.getArmorMaterial() == ItemArmor.ArmorMaterial.LEATHER) {
+					String s = "leather";
+					String s1 = "helmet";
+
+					if (itemarmor.armorType == 0) {
+						s1 = "helmet";
+					}
+
+					if (itemarmor.armorType == 1) {
+						s1 = "chestplate";
+					}
+
+					if (itemarmor.armorType == 2) {
+						s1 = "leggings";
+					}
+
+					if (itemarmor.armorType == 3) {
+						s1 = "boots";
+					}
+
+					String s2 = s + "_" + s1;
+					String s3 = this.getMapTexture(this.mapTextures, "texture." + s2, "items/" + s2);
+					String s4 = this.getMapTexture(this.mapTextures, "texture." + s2 + "_overlay",
+							"items/" + s2 + "_overlay");
+					return new String[] { s3, s4 };
+				}
+			}
+		}
+
+		return new String[] { this.texture };
+	}
+
+	private String getMapTexture(Map<String, String> map, String key, String def) {
+		if (map == null) {
+			return def;
+		} else {
+			String s = (String) map.get(key);
+			return s == null ? def : s;
+		}
+	}
+
+	private static ModelBlock makeModelBlock(String[] modelTextures) {
+		StringBuffer stringbuffer = new StringBuffer();
+		stringbuffer.append("{\"parent\": \"builtin/generated\",\"textures\": {");
+
+		for (int i = 0; i < modelTextures.length; ++i) {
+			String s = modelTextures[i];
+
+			if (i > 0) {
+				stringbuffer.append(", ");
+			}
+
+			stringbuffer.append("\"layer" + i + "\": \"" + s + "\"");
+		}
+
+		stringbuffer.append("}}");
+		String s1 = stringbuffer.toString();
+		ModelBlock modelblock = ModelBlock.deserialize(s1);
+		return modelblock;
+	}
+
+	private static IBakedModel bakeModel(TextureMap textureMap, ModelBlock modelBlockIn, boolean useTint) {
+		ModelRotation modelrotation = ModelRotation.X0_Y0;
+		boolean flag = false;
+		String s = modelBlockIn.resolveTextureName("particle");
+		EaglerTextureAtlasSprite textureatlassprite = textureMap.getAtlasSprite((new ResourceLocation(s)).toString());
+		SimpleBakedModel.Builder simplebakedmodel$builder = (new SimpleBakedModel.Builder(modelBlockIn))
+				.setTexture(textureatlassprite);
+
+		for (BlockPart blockpart : modelBlockIn.getElements()) {
+			for (EnumFacing enumfacing : blockpart.mapFaces.keySet()) {
+				BlockPartFace blockpartface = (BlockPartFace) blockpart.mapFaces.get(enumfacing);
+
+				if (!useTint) {
+					blockpartface = new BlockPartFace(blockpartface.cullFace, -1, blockpartface.texture,
+							blockpartface.blockFaceUV);
+				}
+
+				String s1 = modelBlockIn.resolveTextureName(blockpartface.texture);
+				EaglerTextureAtlasSprite textureatlassprite1 = textureMap
+						.getAtlasSprite((new ResourceLocation(s1)).toString());
+				BakedQuad bakedquad = makeBakedQuad(blockpart, blockpartface, textureatlassprite1, enumfacing,
+						modelrotation, flag);
+
+				if (blockpartface.cullFace == null) {
+					simplebakedmodel$builder.addGeneralQuad(bakedquad);
+				} else {
+					simplebakedmodel$builder.addFaceQuad(modelrotation.rotateFace(blockpartface.cullFace), bakedquad);
+				}
+			}
+		}
+
+		return simplebakedmodel$builder.makeBakedModel();
+	}
+
+	private static BakedQuad makeBakedQuad(BlockPart blockPart, BlockPartFace blockPartFace,
+			EaglerTextureAtlasSprite textureAtlasSprite, EnumFacing enumFacing, ModelRotation modelRotation,
+			boolean uvLocked) {
+		FaceBakery facebakery = new FaceBakery();
+		return facebakery.makeBakedQuad(blockPart.positionFrom, blockPart.positionTo, blockPartFace, textureAtlasSprite,
+				enumFacing, modelRotation, blockPart.partRotation, uvLocked, blockPart.shade);
+	}
+
+	public String toString() {
+		return "" + this.basePath + "/" + this.name + ", type: " + this.type + ", items: ["
+				+ Config.arrayToString(this.items) + "], textture: " + this.texture;
+	}
+
+	public float getTextureWidth(TextureManager textureManager) {
+		if (this.textureWidth <= 0) {
+			if (this.textureLocation != null) {
+				ITextureObject itextureobject = textureManager.getTexture(this.textureLocation);
+				ITextureGL nativeTex = EaglercraftGPU.getNativeTexture(itextureobject.getGlTextureId());
+				this.textureWidth = nativeTex != null ? nativeTex.getWidth() : 0;
+			}
+
+			if (this.textureWidth <= 0) {
+				this.textureWidth = 16;
+			}
+		}
+
+		return (float) this.textureWidth;
+	}
+
+	public float getTextureHeight(TextureManager textureManager) {
+		if (this.textureHeight <= 0) {
+			if (this.textureLocation != null) {
+				ITextureObject itextureobject = textureManager.getTexture(this.textureLocation);
+				ITextureGL nativeTex = EaglercraftGPU.getNativeTexture(itextureobject.getGlTextureId());
+				this.textureHeight = nativeTex != null ? nativeTex.getWidth() : 0;
+			}
+
+			if (this.textureHeight <= 0) {
+				this.textureHeight = 16;
+			}
+		}
+
+		return (float) this.textureHeight;
+	}
+
+	public IBakedModel getBakedModel(ResourceLocation modelLocation, boolean fullModel) {
+		IBakedModel ibakedmodel;
+		Map<String, IBakedModel> map;
+
+		if (fullModel) {
+			ibakedmodel = this.bakedModelFull;
+			map = this.mapBakedModelsFull;
+		} else {
+			ibakedmodel = this.bakedModelTexture;
+			map = this.mapBakedModelsTexture;
+		}
+
+		if (modelLocation != null && map != null) {
+			String s = modelLocation.getResourcePath();
+			IBakedModel ibakedmodel1 = (IBakedModel) map.get(s);
+
+			if (ibakedmodel1 != null) {
+				return ibakedmodel1;
+			}
+		}
+
+		return ibakedmodel;
+	}
+
+	public void loadModels(ModelBakery modelBakery) {
+		if (this.model != null) {
+			loadItemModel(modelBakery, this.model);
+		}
+
+		if (this.type == 1 && this.mapModels != null) {
+			for (String s : this.mapModels.keySet()) {
+				String s1 = (String) this.mapModels.get(s);
+				String s2 = StrUtils.removePrefix(s, "model.");
+
+				if (s2.startsWith("bow") || s2.startsWith("fishing_rod") || s2.startsWith("shield")) {
+					loadItemModel(modelBakery, s1);
+				}
+			}
+		}
+	}
+
+	public void updateModelsFull() {
+		ModelManager modelmanager = Minecraft.getMinecraft().getModelManager();
+		IBakedModel ibakedmodel = modelmanager.getMissingModel();
+
+		if (this.model != null) {
+			ResourceLocation resourcelocation = getModelLocation(this.model);
+			ModelResourceLocation modelresourcelocation = new ModelResourceLocation(resourcelocation, "inventory");
+			this.bakedModelFull = modelmanager.getModel(modelresourcelocation);
+
+			if (this.bakedModelFull == ibakedmodel) {
+				Config.warn("Custom Items: Model not found " + modelresourcelocation.getResourcePath());
+				this.bakedModelFull = null;
+			}
+		}
+
+		if (this.type == 1 && this.mapModels != null) {
+			for (String s : this.mapModels.keySet()) {
+				String s1 = (String) this.mapModels.get(s);
+				String s2 = StrUtils.removePrefix(s, "model.");
+
+				if (s2.startsWith("bow") || s2.startsWith("fishing_rod") || s2.startsWith("shield")) {
+					ResourceLocation resourcelocation1 = getModelLocation(s1);
+					ModelResourceLocation modelresourcelocation1 = new ModelResourceLocation(resourcelocation1,
+							"inventory");
+					IBakedModel ibakedmodel1 = modelmanager.getModel(modelresourcelocation1);
+
+					if (ibakedmodel1 == ibakedmodel) {
+						Config.warn("Custom Items: Model not found " + modelresourcelocation1.getResourcePath());
+					} else {
+						if (this.mapBakedModelsFull == null) {
+							this.mapBakedModelsFull = new HashMap();
+						}
+
+						this.mapBakedModelsFull.put(s2, ibakedmodel1);
+					}
+				}
+			}
+		}
+	}
+
+	private static void loadItemModel(ModelBakery modelBakery, String model) {
+		ResourceLocation resourcelocation = getModelLocation(model);
+		ModelResourceLocation modelresourcelocation = new ModelResourceLocation(resourcelocation, "inventory");
+		modelBakery.loadItemModel(resourcelocation.toString(), modelresourcelocation, resourcelocation);
+	}
+
+	private static ResourceLocation getModelLocation(String modelName) {
+//		return Reflector.ModelLoader.exists() && !modelName.startsWith("mcpatcher/")
+//				&& !modelName.startsWith("optifine/") ? new ResourceLocation("models/" + modelName)
+//						: new ResourceLocation(modelName);
+		return new ResourceLocation(modelName);
+	}
+}
diff --git a/sources/main/java/net/optifine/CustomItems.java b/sources/main/java/net/optifine/CustomItems.java
new file mode 100644
index 00000000..fa394d0d
--- /dev/null
+++ b/sources/main/java/net/optifine/CustomItems.java
@@ -0,0 +1,840 @@
+package net.optifine;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.model.ModelBase;
+import net.minecraft.client.renderer.block.model.ItemModelGenerator;
+import net.minecraft.client.renderer.entity.RenderItem;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.client.resources.IResourcePack;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.client.resources.model.ModelBakery;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemArmor;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.potion.Potion;
+import net.minecraft.util.ResourceLocation;
+import net.optifine.config.NbtTagValue;
+import net.optifine.render.Blender;
+import net.optifine.util.PropertiesOrdered;
+import net.optifine.util.ResUtils;
+import net.optifine.util.StrUtils;
+
+public class CustomItems {
+	private static CustomItemProperties[][] itemProperties = (CustomItemProperties[][]) null;
+	private static CustomItemProperties[][] enchantmentProperties = (CustomItemProperties[][]) null;
+	private static Map mapPotionIds = null;
+	private static ItemModelGenerator itemModelGenerator = new ItemModelGenerator();
+	private static boolean useGlint = true;
+	private static boolean renderOffHand = false;
+	public static final int MASK_POTION_SPLASH = 16384;
+	public static final int MASK_POTION_NAME = 63;
+	public static final int MASK_POTION_EXTENDED = 64;
+	public static final String KEY_TEXTURE_OVERLAY = "texture.potion_overlay";
+	public static final String KEY_TEXTURE_SPLASH = "texture.potion_bottle_splash";
+	public static final String KEY_TEXTURE_DRINKABLE = "texture.potion_bottle_drinkable";
+	public static final String DEFAULT_TEXTURE_OVERLAY = "items/potion_overlay";
+	public static final String DEFAULT_TEXTURE_SPLASH = "items/potion_bottle_splash";
+	public static final String DEFAULT_TEXTURE_DRINKABLE = "items/potion_bottle_drinkable";
+	private static final int[][] EMPTY_INT2_ARRAY = new int[0][];
+	private static final String TYPE_POTION_NORMAL = "normal";
+	private static final String TYPE_POTION_SPLASH = "splash";
+	private static final String TYPE_POTION_LINGER = "linger";
+
+	public static void update() {
+		itemProperties = (CustomItemProperties[][]) null;
+		enchantmentProperties = (CustomItemProperties[][]) null;
+		useGlint = true;
+
+		if (Config.isCustomItems()) {
+			readCitProperties("mcpatcher/cit.properties");
+			IResourcePack[] airesourcepack = Config.getResourcePacks();
+
+			for (int i = airesourcepack.length - 1; i >= 0; --i) {
+				IResourcePack iresourcepack = airesourcepack[i];
+				update(iresourcepack);
+			}
+
+			update(Minecraft.getMinecraft().getDefaultResourcePack());
+
+			if (itemProperties.length <= 0) {
+				itemProperties = (CustomItemProperties[][]) null;
+			}
+
+			if (enchantmentProperties.length <= 0) {
+				enchantmentProperties = (CustomItemProperties[][]) null;
+			}
+		}
+	}
+
+	private static void readCitProperties(String fileName) {
+		ResourceLocation resourcelocation = new ResourceLocation(fileName);
+		try (InputStream inputstream = Minecraft.getMinecraft().getResourceManager().getResource(resourcelocation)
+				.getInputStream()) {
+			Config.dbg("CustomItems: Loading " + fileName);
+			Properties properties = new PropertiesOrdered();
+			properties.load(inputstream);
+			inputstream.close();
+			useGlint = Config.parseBoolean(properties.getProperty("useGlint"), true);
+		} catch (FileNotFoundException var4) {
+			return;
+		} catch (IOException ioexception) {
+			ioexception.printStackTrace();
+		}
+	}
+
+	private static void update(IResourcePack rp) {
+		List<String> astring = ResUtils.collectPropertyFiles(rp, "mcpatcher/cit/", "optifine/cit/");
+		Map map = makeAutoImageProperties(rp);
+
+		if (map.size() > 0) {
+			astring.addAll(map.keySet());
+		}
+
+		Collections.sort(astring);
+		List list = makePropertyList(itemProperties);
+		List list1 = makePropertyList(enchantmentProperties);
+
+		for (int i = 0; i < astring.size(); ++i) {
+			String s = astring.get(i);
+			Config.dbg("CustomItems: " + s);
+
+			try {
+				CustomItemProperties customitemproperties = null;
+
+				if (map.containsKey(s)) {
+					customitemproperties = (CustomItemProperties) map.get(s);
+				}
+
+				if (customitemproperties == null) {
+					ResourceLocation resourcelocation = new ResourceLocation(s);
+					InputStream inputstream = rp.getInputStream(resourcelocation);
+
+					if (inputstream == null) {
+						Config.warn("CustomItems file not found: " + s);
+						continue;
+					}
+
+					Properties properties = new PropertiesOrdered();
+					properties.load(inputstream);
+					inputstream.close();
+					customitemproperties = new CustomItemProperties(properties, s);
+				}
+
+				if (customitemproperties.isValid(s)) {
+					addToItemList(customitemproperties, list);
+					addToEnchantmentList(customitemproperties, list1);
+				}
+			} catch (FileNotFoundException var11) {
+				Config.warn("CustomItems file not found: " + s);
+			} catch (Exception exception) {
+				exception.printStackTrace();
+			}
+		}
+
+		itemProperties = propertyListToArray(list);
+		enchantmentProperties = propertyListToArray(list1);
+		Comparator comparator = getPropertiesComparator();
+
+		for (int j = 0; j < itemProperties.length; ++j) {
+			CustomItemProperties[] acustomitemproperties = itemProperties[j];
+
+			if (acustomitemproperties != null) {
+				Arrays.sort(acustomitemproperties, comparator);
+			}
+		}
+
+		for (int k = 0; k < enchantmentProperties.length; ++k) {
+			CustomItemProperties[] acustomitemproperties1 = enchantmentProperties[k];
+
+			if (acustomitemproperties1 != null) {
+				Arrays.sort(acustomitemproperties1, comparator);
+			}
+		}
+	}
+
+	private static Comparator getPropertiesComparator() {
+		Comparator comparator = new Comparator() {
+			public int compare(Object o1, Object o2) {
+				CustomItemProperties customitemproperties = (CustomItemProperties) o1;
+				CustomItemProperties customitemproperties1 = (CustomItemProperties) o2;
+				return customitemproperties.layer != customitemproperties1.layer
+						? customitemproperties.layer - customitemproperties1.layer
+						: (customitemproperties.weight != customitemproperties1.weight
+								? customitemproperties1.weight - customitemproperties.weight
+								: (!customitemproperties.basePath.equals(customitemproperties1.basePath)
+										? customitemproperties.basePath.compareTo(customitemproperties1.basePath)
+										: customitemproperties.name.compareTo(customitemproperties1.name)));
+			}
+		};
+		return comparator;
+	}
+
+	public static void updateIcons(TextureMap textureMap) {
+		for (CustomItemProperties customitemproperties : getAllProperties()) {
+			customitemproperties.updateIcons(textureMap);
+		}
+	}
+
+	public static void loadModels(ModelBakery modelBakery) {
+		for (CustomItemProperties customitemproperties : getAllProperties()) {
+			customitemproperties.loadModels(modelBakery);
+		}
+	}
+
+	public static void updateModels() {
+		for (CustomItemProperties customitemproperties : getAllProperties()) {
+			if (customitemproperties.type == 1) {
+				TextureMap texturemap = Minecraft.getMinecraft().getTextureMapBlocks();
+				customitemproperties.updateModelTexture(texturemap, itemModelGenerator);
+				customitemproperties.updateModelsFull();
+			}
+		}
+	}
+
+	private static List<CustomItemProperties> getAllProperties() {
+		List<CustomItemProperties> list = new ArrayList();
+		addAll(itemProperties, list);
+		addAll(enchantmentProperties, list);
+		return list;
+	}
+
+	private static void addAll(CustomItemProperties[][] cipsArr, List<CustomItemProperties> list) {
+		if (cipsArr != null) {
+			for (int i = 0; i < cipsArr.length; ++i) {
+				CustomItemProperties[] acustomitemproperties = cipsArr[i];
+
+				if (acustomitemproperties != null) {
+					for (int j = 0; j < acustomitemproperties.length; ++j) {
+						CustomItemProperties customitemproperties = acustomitemproperties[j];
+
+						if (customitemproperties != null) {
+							list.add(customitemproperties);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	private static Map makeAutoImageProperties(IResourcePack rp) {
+		Map map = new HashMap();
+		map.putAll(makePotionImageProperties(rp, "normal", Item.getIdFromItem(Items.potionitem)));
+		map.putAll(makePotionImageProperties(rp, "splash", Item.getIdFromItem(Items.potionitem)));
+		map.putAll(makePotionImageProperties(rp, "linger", Item.getIdFromItem(Items.potionitem)));
+		return map;
+	}
+
+	private static Map makePotionImageProperties(IResourcePack rp, String type, int itemId) {
+		Map map = new HashMap();
+		String s = type + "/";
+		String[] astring = new String[] { "mcpatcher/cit/potion/" + s, "mcpatcher/cit/Potion/" + s, "optifine/cit/potion/" + s };
+		String[] astring1 = new String[] { ".png" };
+		Collection<String> astring2 = ResUtils.collectPotionFiles(rp, astring);
+
+		for (String s1 : astring2) {
+			String name = StrUtils.removePrefixSuffix(s1, astring, astring1);
+			Properties properties = makePotionProperties(name, type, itemId, s1);
+
+			if (properties != null) {
+				String s3 = StrUtils.removeSuffix(s1, astring1) + ".properties";
+				CustomItemProperties customitemproperties = new CustomItemProperties(properties, s3);
+				map.put(s3, customitemproperties);
+			}
+		}
+
+		return map;
+	}
+
+	private static Properties makePotionProperties(String name, String type, int itemId, String path) {
+		if (StrUtils.endsWith(name, new String[] { "_n", "_s" })) {
+			return null;
+		} else if (name.equals("empty") && type.equals("normal")) {
+			itemId = Item.getIdFromItem(Items.glass_bottle);
+			Properties properties = new PropertiesOrdered();
+			properties.put("type", "item");
+			properties.put("items", "" + itemId);
+			return properties;
+		} else {
+			int[] aint = (int[]) ((int[]) getMapPotionIds().get(name));
+
+			if (aint == null) {
+				Config.warn("Potion not found for image: " + path);
+				return null;
+			} else {
+				StringBuffer stringbuffer = new StringBuffer();
+
+				for (int i = 0; i < aint.length; ++i) {
+					int j = aint[i];
+
+					if (type.equals("splash")) {
+						j |= 16384;
+					}
+
+					if (i > 0) {
+						stringbuffer.append(" ");
+					}
+
+					stringbuffer.append(j);
+				}
+
+				int k = 16447;
+
+				if (name.equals("water") || name.equals("mundane")) {
+					k |= 64;
+				}
+
+				Properties properties1 = new PropertiesOrdered();
+				properties1.put("type", "item");
+				properties1.put("items", "" + itemId);
+				properties1.put("damage", "" + stringbuffer.toString());
+				properties1.put("damageMask", "" + k);
+
+				if (type.equals("splash")) {
+					properties1.put("texture.potion_bottle_splash", name);
+				} else {
+					properties1.put("texture.potion_bottle_drinkable", name);
+				}
+
+				return properties1;
+			}
+		}
+	}
+
+	private static Map getMapPotionIds() {
+		if (mapPotionIds == null) {
+			mapPotionIds = new LinkedHashMap();
+			mapPotionIds.put("water", getPotionId(0, 0));
+			mapPotionIds.put("awkward", getPotionId(0, 1));
+			mapPotionIds.put("thick", getPotionId(0, 2));
+			mapPotionIds.put("potent", getPotionId(0, 3));
+			mapPotionIds.put("regeneration", getPotionIds(1));
+			mapPotionIds.put("movespeed", getPotionIds(2));
+			mapPotionIds.put("fireresistance", getPotionIds(3));
+			mapPotionIds.put("poison", getPotionIds(4));
+			mapPotionIds.put("heal", getPotionIds(5));
+			mapPotionIds.put("nightvision", getPotionIds(6));
+			mapPotionIds.put("clear", getPotionId(7, 0));
+			mapPotionIds.put("bungling", getPotionId(7, 1));
+			mapPotionIds.put("charming", getPotionId(7, 2));
+			mapPotionIds.put("rank", getPotionId(7, 3));
+			mapPotionIds.put("weakness", getPotionIds(8));
+			mapPotionIds.put("damageboost", getPotionIds(9));
+			mapPotionIds.put("moveslowdown", getPotionIds(10));
+			mapPotionIds.put("leaping", getPotionIds(11));
+			mapPotionIds.put("harm", getPotionIds(12));
+			mapPotionIds.put("waterbreathing", getPotionIds(13));
+			mapPotionIds.put("invisibility", getPotionIds(14));
+			mapPotionIds.put("thin", getPotionId(15, 0));
+			mapPotionIds.put("debonair", getPotionId(15, 1));
+			mapPotionIds.put("sparkling", getPotionId(15, 2));
+			mapPotionIds.put("stinky", getPotionId(15, 3));
+			mapPotionIds.put("mundane", getPotionId(0, 4));
+			mapPotionIds.put("speed", mapPotionIds.get("movespeed"));
+			mapPotionIds.put("fire_resistance", mapPotionIds.get("fireresistance"));
+			mapPotionIds.put("instant_health", mapPotionIds.get("heal"));
+			mapPotionIds.put("night_vision", mapPotionIds.get("nightvision"));
+			mapPotionIds.put("strength", mapPotionIds.get("damageboost"));
+			mapPotionIds.put("slowness", mapPotionIds.get("moveslowdown"));
+			mapPotionIds.put("instant_damage", mapPotionIds.get("harm"));
+			mapPotionIds.put("water_breathing", mapPotionIds.get("waterbreathing"));
+		}
+
+		return mapPotionIds;
+	}
+
+	private static int[] getPotionIds(int baseId) {
+		return new int[] { baseId, baseId + 16, baseId + 32, baseId + 48 };
+	}
+
+	private static int[] getPotionId(int baseId, int subId) {
+		return new int[] { baseId + subId * 16 };
+	}
+
+	private static int getPotionNameDamage(String name) {
+		String s = "potion." + name;
+		Potion[] apotion = Potion.potionTypes;
+
+		for (int i = 0; i < apotion.length; ++i) {
+			Potion potion = apotion[i];
+
+			if (potion != null) {
+				String s1 = potion.getName();
+
+				if (s.equals(s1)) {
+					return potion.getId();
+				}
+			}
+		}
+
+		return -1;
+	}
+
+	private static List makePropertyList(CustomItemProperties[][] propsArr) {
+		List list = new ArrayList();
+
+		if (propsArr != null) {
+			for (int i = 0; i < propsArr.length; ++i) {
+				CustomItemProperties[] acustomitemproperties = propsArr[i];
+				List list1 = null;
+
+				if (acustomitemproperties != null) {
+					list1 = new ArrayList(Arrays.asList(acustomitemproperties));
+				}
+
+				list.add(list1);
+			}
+		}
+
+		return list;
+	}
+
+	private static CustomItemProperties[][] propertyListToArray(List list) {
+		CustomItemProperties[][] acustomitemproperties = new CustomItemProperties[list.size()][];
+
+		for (int i = 0; i < list.size(); ++i) {
+			List list1 = (List) list.get(i);
+
+			if (list1 != null) {
+				CustomItemProperties[] acustomitemproperties1 = (CustomItemProperties[]) ((CustomItemProperties[]) list1
+						.toArray(new CustomItemProperties[list1.size()]));
+				Arrays.sort(acustomitemproperties1, new CustomItemsComparator());
+				acustomitemproperties[i] = acustomitemproperties1;
+			}
+		}
+
+		return acustomitemproperties;
+	}
+
+	private static void addToItemList(CustomItemProperties cp, List itemList) {
+		if (cp.items != null) {
+			for (int i = 0; i < cp.items.length; ++i) {
+				int j = cp.items[i];
+
+				if (j <= 0) {
+					Config.warn("Invalid item ID: " + j);
+				} else {
+					addToList(cp, itemList, j);
+				}
+			}
+		}
+	}
+
+	private static void addToEnchantmentList(CustomItemProperties cp, List enchantmentList) {
+		if (cp.type == 2) {
+			if (cp.enchantmentIds != null) {
+				for (int i = 0; i < 256; ++i) {
+					if (cp.enchantmentIds.isInRange(i)) {
+						addToList(cp, enchantmentList, i);
+					}
+				}
+			}
+		}
+	}
+
+	private static void addToList(CustomItemProperties cp, List list, int id) {
+		while (id >= list.size()) {
+			list.add(null);
+		}
+
+		List list1 = (List) list.get(id);
+
+		if (list1 == null) {
+			list1 = new ArrayList();
+			list.set(id, list1);
+		}
+
+		list1.add(cp);
+	}
+
+	public static IBakedModel getCustomItemModel(ItemStack itemStack, IBakedModel model, ResourceLocation modelLocation,
+			boolean fullModel) {
+		if (!fullModel && model.isGui3d()) {
+			return model;
+		} else if (itemProperties == null) {
+			return model;
+		} else {
+			CustomItemProperties customitemproperties = getCustomItemProperties(itemStack, 1);
+
+			if (customitemproperties == null) {
+				return model;
+			} else {
+				IBakedModel ibakedmodel = customitemproperties.getBakedModel(modelLocation, fullModel);
+				return ibakedmodel != null ? ibakedmodel : model;
+			}
+		}
+	}
+
+	public static boolean bindCustomArmorTexture(ItemStack itemStack, int layer, String overlay) {
+		if (itemProperties == null) {
+			return false;
+		} else {
+			ResourceLocation resourcelocation = getCustomArmorLocation(itemStack, layer, overlay);
+
+			if (resourcelocation == null) {
+				return false;
+			} else {
+				Minecraft.getMinecraft().getTextureManager().bindTexture(resourcelocation);
+				return true;
+			}
+		}
+	}
+
+	private static ResourceLocation getCustomArmorLocation(ItemStack itemStack, int layer, String overlay) {
+		CustomItemProperties customitemproperties = getCustomItemProperties(itemStack, 3);
+
+		if (customitemproperties == null) {
+			return null;
+		} else if (customitemproperties.mapTextureLocations == null) {
+			return customitemproperties.textureLocation;
+		} else {
+			Item item = itemStack.getItem();
+
+			if (!(item instanceof ItemArmor)) {
+				return null;
+			} else {
+				ItemArmor itemarmor = (ItemArmor) item;
+				String s = itemarmor.getArmorMaterial().getName();
+				StringBuffer stringbuffer = new StringBuffer();
+				stringbuffer.append("texture.");
+				stringbuffer.append(s);
+				stringbuffer.append("_layer_");
+				stringbuffer.append(layer);
+
+				if (overlay != null) {
+					stringbuffer.append("_");
+					stringbuffer.append(overlay);
+				}
+
+				String s1 = stringbuffer.toString();
+				ResourceLocation resourcelocation = (ResourceLocation) customitemproperties.mapTextureLocations.get(s1);
+				return resourcelocation == null ? customitemproperties.textureLocation : resourcelocation;
+			}
+		}
+	}
+
+	private static CustomItemProperties getCustomItemProperties(ItemStack itemStack, int type) {
+		if (itemProperties == null) {
+			return null;
+		} else if (itemStack == null) {
+			return null;
+		} else {
+			Item item = itemStack.getItem();
+			int i = Item.getIdFromItem(item);
+
+			if (i >= 0 && i < itemProperties.length) {
+				CustomItemProperties[] acustomitemproperties = itemProperties[i];
+
+				if (acustomitemproperties != null) {
+					for (int j = 0; j < acustomitemproperties.length; ++j) {
+						CustomItemProperties customitemproperties = acustomitemproperties[j];
+
+						if (customitemproperties.type == type
+								&& matchesProperties(customitemproperties, itemStack, (int[][]) null)) {
+							return customitemproperties;
+						}
+					}
+				}
+			}
+
+			return null;
+		}
+	}
+
+	private static boolean matchesProperties(CustomItemProperties cip, ItemStack itemStack,
+			int[][] enchantmentIdLevels) {
+		Item item = itemStack.getItem();
+
+		if (cip.damage != null) {
+			int i = itemStack.getItemDamage();
+
+			if (cip.damageMask != 0) {
+				i &= cip.damageMask;
+			}
+
+			if (cip.damagePercent) {
+				int j = item.getMaxDamage();
+				i = (int) ((double) (i * 100) / (double) j);
+			}
+
+			if (!cip.damage.isInRange(i)) {
+				return false;
+			}
+		}
+
+		if (cip.stackSize != null && !cip.stackSize.isInRange(itemStack.stackSize)) {
+			return false;
+		} else {
+			int[][] aint = enchantmentIdLevels;
+
+			if (cip.enchantmentIds != null) {
+				if (enchantmentIdLevels == null) {
+					aint = getEnchantmentIdLevels(itemStack);
+				}
+
+				boolean flag = false;
+
+				for (int k = 0; k < aint.length; ++k) {
+					int l = aint[k][0];
+
+					if (cip.enchantmentIds.isInRange(l)) {
+						flag = true;
+						break;
+					}
+				}
+
+				if (!flag) {
+					return false;
+				}
+			}
+
+			if (cip.enchantmentLevels != null) {
+				if (aint == null) {
+					aint = getEnchantmentIdLevels(itemStack);
+				}
+
+				boolean flag1 = false;
+
+				for (int i1 = 0; i1 < aint.length; ++i1) {
+					int k1 = aint[i1][1];
+
+					if (cip.enchantmentLevels.isInRange(k1)) {
+						flag1 = true;
+						break;
+					}
+				}
+
+				if (!flag1) {
+					return false;
+				}
+			}
+
+			if (cip.nbtTagValues != null) {
+				NBTTagCompound nbttagcompound = itemStack.getTagCompound();
+
+				for (int j1 = 0; j1 < cip.nbtTagValues.length; ++j1) {
+					NbtTagValue nbttagvalue = cip.nbtTagValues[j1];
+
+					if (!nbttagvalue.matches(nbttagcompound)) {
+						return false;
+					}
+				}
+			}
+
+			if (cip.hand != 0) {
+				if (cip.hand == 1 && renderOffHand) {
+					return false;
+				}
+
+				if (cip.hand == 2 && !renderOffHand) {
+					return false;
+				}
+			}
+
+			return true;
+		}
+	}
+
+	private static int[][] getEnchantmentIdLevels(ItemStack itemStack) {
+		Item item = itemStack.getItem();
+		NBTTagList nbttaglist = item == Items.enchanted_book ? Items.enchanted_book.getEnchantments(itemStack)
+				: itemStack.getEnchantmentTagList();
+
+		if (nbttaglist != null && nbttaglist.tagCount() > 0) {
+			int[][] aint = new int[nbttaglist.tagCount()][2];
+
+			for (int i = 0; i < nbttaglist.tagCount(); ++i) {
+				NBTTagCompound nbttagcompound = nbttaglist.getCompoundTagAt(i);
+				int j = nbttagcompound.getShort("id");
+				int k = nbttagcompound.getShort("lvl");
+				aint[i][0] = j;
+				aint[i][1] = k;
+			}
+
+			return aint;
+		} else {
+			return EMPTY_INT2_ARRAY;
+		}
+	}
+
+	public static boolean renderCustomEffect(RenderItem renderItem, ItemStack itemStack, IBakedModel model) {
+		if (enchantmentProperties == null) {
+			return false;
+		} else if (itemStack == null) {
+			return false;
+		} else {
+			int[][] aint = getEnchantmentIdLevels(itemStack);
+
+			if (aint.length <= 0) {
+				return false;
+			} else {
+				Set set = null;
+				boolean flag = false;
+				TextureManager texturemanager = Minecraft.getMinecraft().getTextureManager();
+
+				for (int i = 0; i < aint.length; ++i) {
+					int j = aint[i][0];
+
+					if (j >= 0 && j < enchantmentProperties.length) {
+						CustomItemProperties[] acustomitemproperties = enchantmentProperties[j];
+
+						if (acustomitemproperties != null) {
+							for (int k = 0; k < acustomitemproperties.length; ++k) {
+								CustomItemProperties customitemproperties = acustomitemproperties[k];
+
+								if (set == null) {
+									set = new HashSet();
+								}
+
+								if (set.add(Integer.valueOf(j))
+										&& matchesProperties(customitemproperties, itemStack, aint)
+										&& customitemproperties.textureLocation != null) {
+									texturemanager.bindTexture(customitemproperties.textureLocation);
+									float f = customitemproperties.getTextureWidth(texturemanager);
+
+									if (!flag) {
+										flag = true;
+										GlStateManager.depthMask(false);
+										GlStateManager.depthFunc(514);
+										GlStateManager.disableLighting();
+										GlStateManager.matrixMode(5890);
+									}
+
+									Blender.setupBlend(customitemproperties.blend, 1.0F);
+									GlStateManager.pushMatrix();
+									GlStateManager.scale(f / 2.0F, f / 2.0F, f / 2.0F);
+									float f1 = customitemproperties.speed * (float) (Minecraft.getSystemTime() % 3000L)
+											/ 3000.0F / 8.0F;
+									GlStateManager.translate(f1, 0.0F, 0.0F);
+									GlStateManager.rotate(customitemproperties.rotation, 0.0F, 0.0F, 1.0F);
+									renderItem.renderModel(model, -1);
+									GlStateManager.popMatrix();
+								}
+							}
+						}
+					}
+				}
+
+				if (flag) {
+					GlStateManager.enableAlpha();
+					GlStateManager.enableBlend();
+					GlStateManager.blendFunc(770, 771);
+					GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+					GlStateManager.matrixMode(5888);
+					GlStateManager.enableLighting();
+					GlStateManager.depthFunc(515);
+					GlStateManager.depthMask(true);
+					texturemanager.bindTexture(TextureMap.locationBlocksTexture);
+				}
+
+				return flag;
+			}
+		}
+	}
+
+	public static boolean renderCustomArmorEffect(EntityLivingBase entity, ItemStack itemStack, ModelBase model,
+			float limbSwing, float prevLimbSwing, float partialTicks, float timeLimbSwing, float yaw, float pitch,
+			float scale) {
+		if (enchantmentProperties == null) {
+			return false;
+		} else if (itemStack == null) {
+			return false;
+		} else {
+			int[][] aint = getEnchantmentIdLevels(itemStack);
+
+			if (aint.length <= 0) {
+				return false;
+			} else {
+				Set set = null;
+				boolean flag = false;
+				TextureManager texturemanager = Minecraft.getMinecraft().getTextureManager();
+
+				for (int i = 0; i < aint.length; ++i) {
+					int j = aint[i][0];
+
+					if (j >= 0 && j < enchantmentProperties.length) {
+						CustomItemProperties[] acustomitemproperties = enchantmentProperties[j];
+
+						if (acustomitemproperties != null) {
+							for (int k = 0; k < acustomitemproperties.length; ++k) {
+								CustomItemProperties customitemproperties = acustomitemproperties[k];
+
+								if (set == null) {
+									set = new HashSet();
+								}
+
+								if (set.add(Integer.valueOf(j))
+										&& matchesProperties(customitemproperties, itemStack, aint)
+										&& customitemproperties.textureLocation != null) {
+									texturemanager.bindTexture(customitemproperties.textureLocation);
+									float f = customitemproperties.getTextureWidth(texturemanager);
+
+									if (!flag) {
+										flag = true;
+										GlStateManager.enableBlend();
+										GlStateManager.depthFunc(514);
+										GlStateManager.depthMask(false);
+									}
+
+									Blender.setupBlend(customitemproperties.blend, 1.0F);
+									GlStateManager.disableLighting();
+									GlStateManager.matrixMode(5890);
+									GlStateManager.loadIdentity();
+									GlStateManager.rotate(customitemproperties.rotation, 0.0F, 0.0F, 1.0F);
+									float f1 = f / 8.0F;
+									GlStateManager.scale(f1, f1 / 2.0F, f1);
+									float f2 = customitemproperties.speed * (float) (Minecraft.getSystemTime() % 3000L)
+											/ 3000.0F / 8.0F;
+									GlStateManager.translate(0.0F, f2, 0.0F);
+									GlStateManager.matrixMode(5888);
+									model.render(entity, limbSwing, prevLimbSwing, timeLimbSwing, yaw, pitch, scale);
+								}
+							}
+						}
+					}
+				}
+
+				if (flag) {
+					GlStateManager.enableAlpha();
+					GlStateManager.enableBlend();
+					GlStateManager.blendFunc(770, 771);
+					GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+					GlStateManager.matrixMode(5890);
+					GlStateManager.loadIdentity();
+					GlStateManager.matrixMode(5888);
+					GlStateManager.enableLighting();
+					GlStateManager.depthMask(true);
+					GlStateManager.depthFunc(515);
+					GlStateManager.disableBlend();
+				}
+
+				return flag;
+			}
+		}
+	}
+
+	public static boolean isUseGlint() {
+		return useGlint;
+	}
+}
diff --git a/sources/main/java/net/optifine/CustomItemsComparator.java b/sources/main/java/net/optifine/CustomItemsComparator.java
new file mode 100644
index 00000000..175241fe
--- /dev/null
+++ b/sources/main/java/net/optifine/CustomItemsComparator.java
@@ -0,0 +1,15 @@
+package net.optifine;
+
+import java.util.Comparator;
+
+public class CustomItemsComparator implements Comparator {
+	public int compare(Object o1, Object o2) {
+		CustomItemProperties customitemproperties = (CustomItemProperties) o1;
+		CustomItemProperties customitemproperties1 = (CustomItemProperties) o2;
+		return customitemproperties.weight != customitemproperties1.weight
+				? customitemproperties1.weight - customitemproperties.weight
+				: (!Config.equals(customitemproperties.basePath, customitemproperties1.basePath)
+						? customitemproperties.basePath.compareTo(customitemproperties1.basePath)
+						: customitemproperties.name.compareTo(customitemproperties1.name));
+	}
+}
diff --git a/sources/main/java/net/optifine/CustomSky.java b/sources/main/java/net/optifine/CustomSky.java
new file mode 100644
index 00000000..a72c2e24
--- /dev/null
+++ b/sources/main/java/net/optifine/CustomSky.java
@@ -0,0 +1,149 @@
+package net.optifine;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.ITextureObject;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.world.World;
+import net.optifine.render.Blender;
+import net.optifine.util.PropertiesOrdered;
+
+public class CustomSky {
+	private static CustomSkyLayer[][] worldSkyLayers = (CustomSkyLayer[][]) null;
+
+	public static void reset() {
+		worldSkyLayers = (CustomSkyLayer[][]) null;
+	}
+
+	public static void update() {
+		reset();
+
+		if (Config.isCustomSky()) {
+			worldSkyLayers = readCustomSkies();
+		}
+	}
+
+	private static CustomSkyLayer[][] readCustomSkies() {
+		CustomSkyLayer[][] acustomskylayer = new CustomSkyLayer[10][0];
+		String s = "mcpatcher/sky/world";
+		int i = -1;
+
+		for (int j = 0; j < acustomskylayer.length; ++j) {
+			String s1 = s + j + "/sky";
+			List list = new ArrayList();
+
+			for (int k = 1; k < 1000; ++k) {
+				String s2 = s1 + k + ".properties";
+				ResourceLocation resourcelocation = new ResourceLocation(s2);
+
+				try (InputStream inputstream = Minecraft.getMinecraft().getResourceManager()
+						.getResource(resourcelocation).getInputStream()) {
+					if (inputstream == null) {
+						break;
+					}
+
+					Properties properties = new PropertiesOrdered();
+					properties.load(inputstream);
+					inputstream.close();
+					Config.dbg("CustomSky properties: " + s2);
+					String s3 = s1 + k + ".png";
+					CustomSkyLayer customskylayer = new CustomSkyLayer(properties, s3);
+
+					if (customskylayer.isValid(s2)) {
+						ResourceLocation resourcelocation1 = new ResourceLocation(customskylayer.source);
+						TextureManager mgr = Minecraft.getMinecraft().getTextureManager();
+						mgr.bindTexture(resourcelocation1);
+						ITextureObject itextureobject = mgr.getTexture(resourcelocation1);
+
+						if (itextureobject == null) {
+							Config.warn("CustomSky: Texture not found: " + resourcelocation1);
+						} else {
+							customskylayer.textureId = itextureobject.getGlTextureId();
+							list.add(customskylayer);
+							inputstream.close();
+						}
+					}
+				} catch (FileNotFoundException var15) {
+					break;
+				} catch (IOException ioexception) {
+					ioexception.printStackTrace();
+				}
+			}
+
+			if (list.size() > 0) {
+				CustomSkyLayer[] acustomskylayer2 = (CustomSkyLayer[]) ((CustomSkyLayer[]) list
+						.toArray(new CustomSkyLayer[list.size()]));
+				acustomskylayer[j] = acustomskylayer2;
+				i = j;
+			}
+		}
+
+		if (i < 0) {
+			return (CustomSkyLayer[][]) null;
+		} else {
+			int l = i + 1;
+			CustomSkyLayer[][] acustomskylayer1 = new CustomSkyLayer[l][0];
+
+			for (int i1 = 0; i1 < acustomskylayer1.length; ++i1) {
+				acustomskylayer1[i1] = acustomskylayer[i1];
+			}
+
+			return acustomskylayer1;
+		}
+	}
+
+	public static void renderSky(World world, TextureManager re, float partialTicks) {
+		if (worldSkyLayers != null) {
+			int i = world.provider.getDimensionId();
+
+			if (i >= 0 && i < worldSkyLayers.length) {
+				CustomSkyLayer[] acustomskylayer = worldSkyLayers[i];
+
+				if (acustomskylayer != null) {
+					long j = world.getWorldTime();
+					int k = (int) (j % 24000L);
+					float f = world.getCelestialAngle(partialTicks);
+					float f1 = world.getRainStrength(partialTicks);
+					float f2 = world.getThunderStrength(partialTicks);
+
+					if (f1 > 0.0F) {
+						f2 /= f1;
+					}
+
+					for (int l = 0; l < acustomskylayer.length; ++l) {
+						CustomSkyLayer customskylayer = acustomskylayer[l];
+
+						if (customskylayer.isActive(world, k)) {
+							customskylayer.render(world, k, f, f1, f2);
+						}
+					}
+
+					float f3 = 1.0F - f1;
+					Blender.clearBlend(f3);
+				}
+			}
+		}
+	}
+
+	public static boolean hasSkyLayers(World world) {
+		if (worldSkyLayers == null) {
+			return false;
+		} else {
+			int i = world.provider.getDimensionId();
+
+			if (i >= 0 && i < worldSkyLayers.length) {
+				CustomSkyLayer[] acustomskylayer = worldSkyLayers[i];
+				return acustomskylayer == null ? false : acustomskylayer.length > 0;
+			} else {
+				return false;
+			}
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/CustomSkyLayer.java b/sources/main/java/net/optifine/CustomSkyLayer.java
new file mode 100644
index 00000000..4927fe7b
--- /dev/null
+++ b/sources/main/java/net/optifine/CustomSkyLayer.java
@@ -0,0 +1,434 @@
+package net.optifine;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
+import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import net.minecraft.entity.Entity;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.MathHelper;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.optifine.config.ConnectedParser;
+import net.optifine.config.Matches;
+import net.optifine.config.RangeListInt;
+import net.optifine.render.Blender;
+import net.optifine.util.NumUtils;
+import net.optifine.util.SmoothFloat;
+import net.optifine.util.TextureUtils;
+
+public class CustomSkyLayer {
+	public String source = null;
+	private int startFadeIn = -1;
+	private int endFadeIn = -1;
+	private int startFadeOut = -1;
+	private int endFadeOut = -1;
+	private int blend = 1;
+	private boolean rotate = false;
+	private float speed = 1.0F;
+	private float[] axis;
+	private RangeListInt days;
+	private int daysLoop;
+	private boolean weatherClear;
+	private boolean weatherRain;
+	private boolean weatherThunder;
+	public BiomeGenBase[] biomes;
+	public RangeListInt heights;
+	private float transition;
+	private SmoothFloat smoothPositionBrightness;
+	public int textureId;
+	private World lastWorld;
+	public static final float[] DEFAULT_AXIS = new float[] { 1.0F, 0.0F, 0.0F };
+	private static final String WEATHER_CLEAR = "clear";
+	private static final String WEATHER_RAIN = "rain";
+	private static final String WEATHER_THUNDER = "thunder";
+
+	public CustomSkyLayer(Properties props, String defSource) {
+		this.axis = DEFAULT_AXIS;
+		this.days = null;
+		this.daysLoop = 8;
+		this.weatherClear = true;
+		this.weatherRain = false;
+		this.weatherThunder = false;
+		this.biomes = null;
+		this.heights = null;
+		this.transition = 1.0F;
+		this.smoothPositionBrightness = null;
+		this.textureId = -1;
+		this.lastWorld = null;
+		ConnectedParser connectedparser = new ConnectedParser("CustomSky");
+		this.source = props.getProperty("source", defSource);
+		this.startFadeIn = this.parseTime(props.getProperty("startFadeIn"));
+		this.endFadeIn = this.parseTime(props.getProperty("endFadeIn"));
+		this.startFadeOut = this.parseTime(props.getProperty("startFadeOut"));
+		this.endFadeOut = this.parseTime(props.getProperty("endFadeOut"));
+		this.blend = Blender.parseBlend(props.getProperty("blend"));
+		this.rotate = this.parseBoolean(props.getProperty("rotate"), true);
+		this.speed = this.parseFloat(props.getProperty("speed"), 1.0F);
+		this.axis = this.parseAxis(props.getProperty("axis"), DEFAULT_AXIS);
+		this.days = connectedparser.parseRangeListInt(props.getProperty("days"));
+		this.daysLoop = connectedparser.parseInt(props.getProperty("daysLoop"), 8);
+		List<String> list = this.parseWeatherList(props.getProperty("weather", "clear"));
+		this.weatherClear = list.contains("clear");
+		this.weatherRain = list.contains("rain");
+		this.weatherThunder = list.contains("thunder");
+		this.biomes = connectedparser.parseBiomes(props.getProperty("biomes"));
+		this.heights = connectedparser.parseRangeListInt(props.getProperty("heights"));
+		this.transition = this.parseFloat(props.getProperty("transition"), 1.0F);
+	}
+
+	private List<String> parseWeatherList(String str) {
+		List<String> list = Arrays.<String>asList(new String[] { "clear", "rain", "thunder" });
+		List<String> list1 = new ArrayList();
+		String[] astring = Config.tokenize(str, " ");
+
+		for (int i = 0; i < astring.length; ++i) {
+			String s = astring[i];
+
+			if (!list.contains(s)) {
+				Config.warn("Unknown weather: " + s);
+			} else {
+				list1.add(s);
+			}
+		}
+
+		return list1;
+	}
+
+	private int parseTime(String str) {
+		if (str == null) {
+			return -1;
+		} else {
+			String[] astring = Config.tokenize(str, ":");
+
+			if (astring.length != 2) {
+				Config.warn("Invalid time: " + str);
+				return -1;
+			} else {
+				String s = astring[0];
+				String s1 = astring[1];
+				int i = Config.parseInt(s, -1);
+				int j = Config.parseInt(s1, -1);
+
+				if (i >= 0 && i <= 23 && j >= 0 && j <= 59) {
+					i = i - 6;
+
+					if (i < 0) {
+						i += 24;
+					}
+
+					int k = i * 1000 + (int) ((double) j / 60.0D * 1000.0D);
+					return k;
+				} else {
+					Config.warn("Invalid time: " + str);
+					return -1;
+				}
+			}
+		}
+	}
+
+	private boolean parseBoolean(String str, boolean defVal) {
+		if (str == null) {
+			return defVal;
+		} else if (str.toLowerCase().equals("true")) {
+			return true;
+		} else if (str.toLowerCase().equals("false")) {
+			return false;
+		} else {
+			Config.warn("Unknown boolean: " + str);
+			return defVal;
+		}
+	}
+
+	private float parseFloat(String str, float defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			float f = Config.parseFloat(str, Float.MIN_VALUE);
+
+			if (f == Float.MIN_VALUE) {
+				Config.warn("Invalid value: " + str);
+				return defVal;
+			} else {
+				return f;
+			}
+		}
+	}
+
+	private float[] parseAxis(String str, float[] defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			String[] astring = Config.tokenize(str, " ");
+
+			if (astring.length != 3) {
+				Config.warn("Invalid axis: " + str);
+				return defVal;
+			} else {
+				float[] afloat = new float[3];
+
+				for (int i = 0; i < astring.length; ++i) {
+					afloat[i] = Config.parseFloat(astring[i], Float.MIN_VALUE);
+
+					if (afloat[i] == Float.MIN_VALUE) {
+						Config.warn("Invalid axis: " + str);
+						return defVal;
+					}
+
+					if (afloat[i] < -1.0F || afloat[i] > 1.0F) {
+						Config.warn("Invalid axis values: " + str);
+						return defVal;
+					}
+				}
+
+				float f2 = afloat[0];
+				float f = afloat[1];
+				float f1 = afloat[2];
+
+				float l = f2 * f2 + f * f + f1 * f1;
+				if (l < 1.0E-5F) {
+					Config.warn("Invalid axis values: " + str);
+					return defVal;
+				} else {
+					l = MathHelper.sqrt_float(l);
+					return new float[] { f1 / l, f / l, -f2 / l };
+				}
+			}
+		}
+	}
+
+	public boolean isValid(String path) {
+		if (this.source == null) {
+			Config.warn("No source texture: " + path);
+			return false;
+		} else {
+			this.source = TextureUtils.fixResourcePath(this.source, TextureUtils.getBasePath(path));
+
+			if (this.startFadeIn >= 0 && this.endFadeIn >= 0 && this.endFadeOut >= 0) {
+				int i = this.normalizeTime(this.endFadeIn - this.startFadeIn);
+
+				if (this.startFadeOut < 0) {
+					this.startFadeOut = this.normalizeTime(this.endFadeOut - i);
+
+					if (this.timeBetween(this.startFadeOut, this.startFadeIn, this.endFadeIn)) {
+						this.startFadeOut = this.endFadeIn;
+					}
+				}
+
+				int j = this.normalizeTime(this.startFadeOut - this.endFadeIn);
+				int k = this.normalizeTime(this.endFadeOut - this.startFadeOut);
+				int l = this.normalizeTime(this.startFadeIn - this.endFadeOut);
+				int i1 = i + j + k + l;
+
+				if (i1 != 24000) {
+					Config.warn("Invalid fadeIn/fadeOut times, sum is not 24h: " + i1);
+					return false;
+				} else if (this.speed < 0.0F) {
+					Config.warn("Invalid speed: " + this.speed);
+					return false;
+				} else if (this.daysLoop <= 0) {
+					Config.warn("Invalid daysLoop: " + this.daysLoop);
+					return false;
+				} else {
+					return true;
+				}
+			} else {
+				Config.warn("Invalid times, required are: startFadeIn, endFadeIn and endFadeOut.");
+				return false;
+			}
+		}
+	}
+
+	private int normalizeTime(int timeMc) {
+		while (timeMc >= 24000) {
+			timeMc -= 24000;
+		}
+
+		while (timeMc < 0) {
+			timeMc += 24000;
+		}
+
+		return timeMc;
+	}
+
+	public void render(World world, int timeOfDay, float celestialAngle, float rainStrength, float thunderStrength) {
+		float f = this.getPositionBrightness(world);
+		float f1 = this.getWeatherBrightness(rainStrength, thunderStrength);
+		float f2 = this.getFadeBrightness(timeOfDay);
+		float f3 = f * f1 * f2;
+		f3 = Config.limit(f3, 0.0F, 1.0F);
+
+		if (f3 >= 1.0E-4F) {
+			GlStateManager.bindTexture(this.textureId);
+			Blender.setupBlend(this.blend, f3);
+			GlStateManager.pushMatrix();
+
+			if (this.rotate) {
+				float f4 = 0.0F;
+
+				if (this.speed != (float) Math.round(this.speed)) {
+					long i = (world.getWorldTime() + 18000L) / 24000L;
+					double d0 = (double) (this.speed % 1.0F);
+					double d1 = (double) i * d0;
+					f4 = (float) (d1 % 1.0D);
+				}
+
+				GlStateManager.rotate(360.0F * (f4 + celestialAngle * this.speed), this.axis[0], this.axis[1],
+						this.axis[2]);
+			}
+
+			Tessellator tessellator = Tessellator.getInstance();
+			GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F);
+			GlStateManager.rotate(-90.0F, 0.0F, 0.0F, 1.0F);
+			this.renderSide(tessellator, 4);
+			GlStateManager.pushMatrix();
+			GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F);
+			this.renderSide(tessellator, 1);
+			GlStateManager.popMatrix();
+			GlStateManager.pushMatrix();
+			GlStateManager.rotate(-90.0F, 1.0F, 0.0F, 0.0F);
+			this.renderSide(tessellator, 0);
+			GlStateManager.popMatrix();
+			GlStateManager.rotate(90.0F, 0.0F, 0.0F, 1.0F);
+			this.renderSide(tessellator, 5);
+			GlStateManager.rotate(90.0F, 0.0F, 0.0F, 1.0F);
+			this.renderSide(tessellator, 2);
+			GlStateManager.rotate(90.0F, 0.0F, 0.0F, 1.0F);
+			this.renderSide(tessellator, 3);
+			GlStateManager.popMatrix();
+		}
+	}
+
+	private float getPositionBrightness(World world) {
+		if (this.biomes == null && this.heights == null) {
+			return 1.0F;
+		} else {
+			float f = this.getPositionBrightnessRaw(world);
+
+			if (this.smoothPositionBrightness == null) {
+				this.smoothPositionBrightness = new SmoothFloat(f, this.transition);
+			}
+
+			f = this.smoothPositionBrightness.getSmoothValue(f);
+			return f;
+		}
+	}
+
+	private float getPositionBrightnessRaw(World world) {
+		Entity entity = Minecraft.getMinecraft().getRenderViewEntity();
+
+		if (entity == null) {
+			return 0.0F;
+		} else {
+			BlockPos blockpos = entity.getPosition();
+
+			if (this.biomes != null) {
+				BiomeGenBase biomegenbase = world.getBiomeGenForCoords(blockpos);
+
+				if (biomegenbase == null) {
+					return 0.0F;
+				}
+
+				if (!Matches.biome(biomegenbase, this.biomes)) {
+					return 0.0F;
+				}
+			}
+
+			return this.heights != null && !this.heights.isInRange(blockpos.getY()) ? 0.0F : 1.0F;
+		}
+	}
+
+	private float getWeatherBrightness(float rainStrength, float thunderStrength) {
+		float f = 1.0F - rainStrength;
+		float f1 = rainStrength - thunderStrength;
+		float f2 = 0.0F;
+
+		if (this.weatherClear) {
+			f2 += f;
+		}
+
+		if (this.weatherRain) {
+			f2 += f1;
+		}
+
+		if (this.weatherThunder) {
+			f2 += thunderStrength;
+		}
+
+		f2 = NumUtils.limit(f2, 0.0F, 1.0F);
+		return f2;
+	}
+
+	private float getFadeBrightness(int timeOfDay) {
+		if (this.timeBetween(timeOfDay, this.startFadeIn, this.endFadeIn)) {
+			int k = this.normalizeTime(this.endFadeIn - this.startFadeIn);
+			int l = this.normalizeTime(timeOfDay - this.startFadeIn);
+			return (float) l / (float) k;
+		} else if (this.timeBetween(timeOfDay, this.endFadeIn, this.startFadeOut)) {
+			return 1.0F;
+		} else if (this.timeBetween(timeOfDay, this.startFadeOut, this.endFadeOut)) {
+			int i = this.normalizeTime(this.endFadeOut - this.startFadeOut);
+			int j = this.normalizeTime(timeOfDay - this.startFadeOut);
+			return 1.0F - (float) j / (float) i;
+		} else {
+			return 0.0F;
+		}
+	}
+
+	private void renderSide(Tessellator tess, int side) {
+		WorldRenderer worldrenderer = tess.getWorldRenderer();
+		double d0 = (double) (side % 3) / 3.0D;
+		double d1 = (double) (side / 3) / 2.0D;
+		worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
+		worldrenderer.pos(-100.0D, -100.0D, -100.0D).tex(d0, d1).endVertex();
+		worldrenderer.pos(-100.0D, -100.0D, 100.0D).tex(d0, d1 + 0.5D).endVertex();
+		worldrenderer.pos(100.0D, -100.0D, 100.0D).tex(d0 + 0.3333333333333333D, d1 + 0.5D).endVertex();
+		worldrenderer.pos(100.0D, -100.0D, -100.0D).tex(d0 + 0.3333333333333333D, d1).endVertex();
+		tess.draw();
+	}
+
+	public boolean isActive(World world, int timeOfDay) {
+		if (world != this.lastWorld) {
+			this.lastWorld = world;
+			this.smoothPositionBrightness = null;
+		}
+
+		if (this.timeBetween(timeOfDay, this.endFadeOut, this.startFadeIn)) {
+			return false;
+		} else {
+			if (this.days != null) {
+				long i = world.getWorldTime();
+				long j;
+
+				for (j = i - (long) this.startFadeIn; j < 0L; j += (long) (24000 * this.daysLoop)) {
+					;
+				}
+
+				int k = (int) (j / 24000L);
+				int l = k % this.daysLoop;
+
+				if (!this.days.isInRange(l)) {
+					return false;
+				}
+			}
+
+			return true;
+		}
+	}
+
+	private boolean timeBetween(int timeOfDay, int timeStart, int timeEnd) {
+		return timeStart <= timeEnd ? timeOfDay >= timeStart && timeOfDay <= timeEnd
+				: timeOfDay >= timeStart || timeOfDay <= timeEnd;
+	}
+
+	public String toString() {
+		return "" + this.source + ", " + this.startFadeIn + "-" + this.endFadeIn + " " + this.startFadeOut + "-"
+				+ this.endFadeOut;
+	}
+}
diff --git a/sources/main/java/net/optifine/SmartLeaves.java b/sources/main/java/net/optifine/SmartLeaves.java
new file mode 100644
index 00000000..6bead825
--- /dev/null
+++ b/sources/main/java/net/optifine/SmartLeaves.java
@@ -0,0 +1,198 @@
+package net.optifine;
+
+import java.util.ArrayList;
+import java.util.List;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockNewLeaf;
+import net.minecraft.block.BlockOldLeaf;
+import net.minecraft.block.BlockPlanks;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.resources.DefaultResourcePack;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.client.resources.model.ModelManager;
+import net.minecraft.client.resources.model.ModelResourceLocation;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.optifine.model.ModelUtils;
+
+public class SmartLeaves {
+	private static IBakedModel modelLeavesCullAcacia = null;
+	private static IBakedModel modelLeavesCullBirch = null;
+	private static IBakedModel modelLeavesCullDarkOak = null;
+	private static IBakedModel modelLeavesCullJungle = null;
+	private static IBakedModel modelLeavesCullOak = null;
+	private static IBakedModel modelLeavesCullSpruce = null;
+	private static List generalQuadsCullAcacia = null;
+	private static List generalQuadsCullBirch = null;
+	private static List generalQuadsCullDarkOak = null;
+	private static List generalQuadsCullJungle = null;
+	private static List generalQuadsCullOak = null;
+	private static List generalQuadsCullSpruce = null;
+	private static IBakedModel modelLeavesDoubleAcacia = null;
+	private static IBakedModel modelLeavesDoubleBirch = null;
+	private static IBakedModel modelLeavesDoubleDarkOak = null;
+	private static IBakedModel modelLeavesDoubleJungle = null;
+	private static IBakedModel modelLeavesDoubleOak = null;
+	private static IBakedModel modelLeavesDoubleSpruce = null;
+
+	public static IBakedModel getLeavesModel(IBakedModel model, IBlockState stateIn) {
+		if (!Config.isTreesSmart()) {
+			return model;
+		} else {
+			List list = model.getGeneralQuads();
+			return list == generalQuadsCullAcacia ? modelLeavesDoubleAcacia
+					: (list == generalQuadsCullBirch ? modelLeavesDoubleBirch
+							: (list == generalQuadsCullDarkOak ? modelLeavesDoubleDarkOak
+									: (list == generalQuadsCullJungle ? modelLeavesDoubleJungle
+											: (list == generalQuadsCullOak ? modelLeavesDoubleOak
+													: (list == generalQuadsCullSpruce ? modelLeavesDoubleSpruce
+															: model)))));
+		}
+	}
+
+	public static boolean isSameLeaves(IBlockState state1, IBlockState state2) {
+		if (state1 == state2) {
+			return true;
+		} else {
+			Block block = state1.getBlock();
+			Block block1 = state2.getBlock();
+			return block != block1 ? false
+					: (block instanceof BlockOldLeaf
+							? ((BlockPlanks.EnumType) state1.getValue(BlockOldLeaf.VARIANT))
+									.equals(state2.getValue(BlockOldLeaf.VARIANT))
+							: (block instanceof BlockNewLeaf
+									? ((BlockPlanks.EnumType) state1.getValue(BlockNewLeaf.VARIANT)).equals(
+											state2.getValue(BlockNewLeaf.VARIANT))
+									: false));
+		}
+	}
+
+	public static void updateLeavesModels() {
+		List list = new ArrayList();
+		modelLeavesCullAcacia = getModelCull("acacia", list);
+		modelLeavesCullBirch = getModelCull("birch", list);
+		modelLeavesCullDarkOak = getModelCull("dark_oak", list);
+		modelLeavesCullJungle = getModelCull("jungle", list);
+		modelLeavesCullOak = getModelCull("oak", list);
+		modelLeavesCullSpruce = getModelCull("spruce", list);
+		generalQuadsCullAcacia = getGeneralQuadsSafe(modelLeavesCullAcacia);
+		generalQuadsCullBirch = getGeneralQuadsSafe(modelLeavesCullBirch);
+		generalQuadsCullDarkOak = getGeneralQuadsSafe(modelLeavesCullDarkOak);
+		generalQuadsCullJungle = getGeneralQuadsSafe(modelLeavesCullJungle);
+		generalQuadsCullOak = getGeneralQuadsSafe(modelLeavesCullOak);
+		generalQuadsCullSpruce = getGeneralQuadsSafe(modelLeavesCullSpruce);
+		modelLeavesDoubleAcacia = getModelDoubleFace(modelLeavesCullAcacia);
+		modelLeavesDoubleBirch = getModelDoubleFace(modelLeavesCullBirch);
+		modelLeavesDoubleDarkOak = getModelDoubleFace(modelLeavesCullDarkOak);
+		modelLeavesDoubleJungle = getModelDoubleFace(modelLeavesCullJungle);
+		modelLeavesDoubleOak = getModelDoubleFace(modelLeavesCullOak);
+		modelLeavesDoubleSpruce = getModelDoubleFace(modelLeavesCullSpruce);
+
+		if (list.size() > 0) {
+			Config.dbg("Enable face culling: " + Config.arrayToString(list.toArray()));
+		}
+	}
+
+	private static List getGeneralQuadsSafe(IBakedModel model) {
+		return model == null ? null : model.getGeneralQuads();
+	}
+
+	static IBakedModel getModelCull(String type, List updatedTypes) {
+		ModelManager modelmanager = Minecraft.getMinecraft().getModelManager();
+
+		if (modelmanager == null) {
+			return null;
+		} else {
+			ResourceLocation resourcelocation = new ResourceLocation("blockstates/" + type + "_leaves.json");
+
+			DefaultResourcePack res = Minecraft.getMinecraft().getDefaultResourcePack();
+			if (Config.getDefiningResourcePack(resourcelocation) != res) {
+				return null;
+			} else {
+				ResourceLocation resourcelocation1 = new ResourceLocation("models/block/" + type + "_leaves.json");
+
+				if (Config.getDefiningResourcePack(resourcelocation1) != res) {
+					return null;
+				} else {
+					ModelResourceLocation modelresourcelocation = new ModelResourceLocation(type + "_leaves", "normal");
+					IBakedModel ibakedmodel = modelmanager.getModel(modelresourcelocation);
+
+					if (ibakedmodel != null && ibakedmodel != modelmanager.getMissingModel()) {
+						List list = ibakedmodel.getGeneralQuads();
+
+						if (list.size() == 0) {
+							return ibakedmodel;
+						} else if (list.size() != 6) {
+							return null;
+						} else {
+							for (Object bakedquad0 : list) {
+								BakedQuad bakedquad = (BakedQuad) bakedquad0;
+								List list1 = ibakedmodel.getFaceQuads(bakedquad.getFace());
+
+								if (list1.size() > 0) {
+									return null;
+								}
+
+								list1.add(bakedquad);
+							}
+
+							list.clear();
+							updatedTypes.add(type + "_leaves");
+							return ibakedmodel;
+						}
+					} else {
+						return null;
+					}
+				}
+			}
+		}
+	}
+
+	private static IBakedModel getModelDoubleFace(IBakedModel model) {
+		if (model == null) {
+			return null;
+		} else if (model.getGeneralQuads().size() > 0) {
+			Config.warn("SmartLeaves: Model is not cube, general quads: " + model.getGeneralQuads().size() + ", model: "
+					+ model);
+			return model;
+		} else {
+			EnumFacing[] aenumfacing = EnumFacing._VALUES;
+
+			for (int i = 0; i < aenumfacing.length; ++i) {
+				EnumFacing enumfacing = aenumfacing[i];
+				List<BakedQuad> list = model.getFaceQuads(enumfacing);
+
+				if (list.size() != 1) {
+					Config.warn("SmartLeaves: Model is not cube, side: " + enumfacing + ", quads: " + list.size()
+							+ ", model: " + model);
+					return model;
+				}
+			}
+
+			IBakedModel ibakedmodel = ModelUtils.duplicateModel(model);
+			List[] alist = new List[aenumfacing.length];
+
+			for (int k = 0; k < aenumfacing.length; ++k) {
+				EnumFacing enumfacing1 = aenumfacing[k];
+				List<BakedQuad> list1 = ibakedmodel.getFaceQuads(enumfacing1);
+				BakedQuad bakedquad = (BakedQuad) list1.get(0);
+				BakedQuad bakedquad1 = new BakedQuad((int[]) bakedquad.getVertexData().clone(),
+						(int[]) bakedquad.getVertexDataWithNormals().clone(), bakedquad.getTintIndex(),
+						bakedquad.getFace(), bakedquad.getSprite());
+				int[] aint = bakedquad1.getVertexData();
+				int[] aint1 = (int[]) aint.clone();
+				int j = aint.length / 4;
+				System.arraycopy(aint, 0 * j, aint1, 3 * j, j);
+				System.arraycopy(aint, 1 * j, aint1, 2 * j, j);
+				System.arraycopy(aint, 2 * j, aint1, 1 * j, j);
+				System.arraycopy(aint, 3 * j, aint1, 0 * j, j);
+				System.arraycopy(aint1, 0, aint, 0, aint1.length);
+				list1.add(bakedquad1);
+			}
+
+			return ibakedmodel;
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/config/ConnectedParser.java b/sources/main/java/net/optifine/config/ConnectedParser.java
new file mode 100644
index 00000000..fe25b95f
--- /dev/null
+++ b/sources/main/java/net/optifine/config/ConnectedParser.java
@@ -0,0 +1,914 @@
+package net.optifine.config;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockDoublePlant;
+import net.minecraft.block.properties.IProperty;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.item.Item;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumWorldBlockLayer;
+import net.minecraft.util.IStringSerializable;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.optifine.Config;
+import net.optifine.ConnectedProperties;
+
+public class ConnectedParser {
+	private String context = null;
+	public static final EnumDyeColor[] DYE_COLORS_INVALID = new EnumDyeColor[0];
+	private static final INameGetter<Enum> NAME_GETTER_ENUM = new INameGetter<Enum>() {
+		public String getName(Enum en) {
+			return en.name();
+		}
+	};
+	private static final INameGetter<EnumDyeColor> NAME_GETTER_DYE_COLOR = new INameGetter<EnumDyeColor>() {
+		public String getName(EnumDyeColor col) {
+			return col.getName();
+		}
+	};
+
+	public ConnectedParser(String context) {
+		this.context = context;
+	}
+
+	public String parseName(String path) {
+		String s = path;
+		int i = path.lastIndexOf(47);
+
+		if (i >= 0) {
+			s = path.substring(i + 1);
+		}
+
+		int j = s.lastIndexOf(46);
+
+		if (j >= 0) {
+			s = s.substring(0, j);
+		}
+
+		return s;
+	}
+
+	public String parseBasePath(String path) {
+		int i = path.lastIndexOf(47);
+		return i < 0 ? "" : path.substring(0, i);
+	}
+
+	public MatchBlock[] parseMatchBlocks(String propMatchBlocks) {
+		if (propMatchBlocks == null) {
+			return null;
+		} else {
+			List list = new ArrayList();
+			String[] astring = Config.tokenize(propMatchBlocks, " ");
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+				MatchBlock[] amatchblock = this.parseMatchBlock(s);
+
+				if (amatchblock != null) {
+					list.addAll(Arrays.asList(amatchblock));
+				}
+			}
+
+			MatchBlock[] amatchblock1 = (MatchBlock[]) ((MatchBlock[]) list.toArray(new MatchBlock[list.size()]));
+			return amatchblock1;
+		}
+	}
+
+	public IBlockState parseBlockState(String str, IBlockState def) {
+		MatchBlock[] amatchblock = this.parseMatchBlock(str);
+
+		if (amatchblock == null) {
+			return def;
+		} else if (amatchblock.length != 1) {
+			return def;
+		} else {
+			MatchBlock matchblock = amatchblock[0];
+			int i = matchblock.getBlockId();
+			Block block = Block.getBlockById(i);
+			return block.getDefaultState();
+		}
+	}
+
+	public MatchBlock[] parseMatchBlock(String blockStr) {
+		if (blockStr == null) {
+			return null;
+		} else {
+			blockStr = blockStr.trim();
+
+			if (blockStr.length() <= 0) {
+				return null;
+			} else {
+				String[] astring = Config.tokenize(blockStr, ":");
+				String s = "minecraft";
+				int i = 0;
+
+				if (astring.length > 1 && this.isFullBlockName(astring)) {
+					s = astring[0];
+					i = 1;
+				} else {
+					s = "minecraft";
+					i = 0;
+				}
+
+				String s1 = astring[i];
+				String[] astring1 = (String[]) Arrays.copyOfRange(astring, i + 1, astring.length);
+				Block[] ablock = this.parseBlockPart(s, s1);
+
+				if (ablock == null) {
+					return null;
+				} else {
+					MatchBlock[] amatchblock = new MatchBlock[ablock.length];
+
+					for (int j = 0; j < ablock.length; ++j) {
+						Block block = ablock[j];
+						int k = Block.getIdFromBlock(block);
+						int[] aint = null;
+
+						if (astring1.length > 0) {
+							aint = this.parseBlockMetadatas(block, astring1);
+
+							if (aint == null) {
+								return null;
+							}
+						}
+
+						MatchBlock matchblock = new MatchBlock(k, aint);
+						amatchblock[j] = matchblock;
+					}
+
+					return amatchblock;
+				}
+			}
+		}
+	}
+
+	public boolean isFullBlockName(String[] parts) {
+		if (parts.length < 2) {
+			return false;
+		} else {
+			String s = parts[1];
+			return s.length() < 1 ? false : (this.startsWithDigit(s) ? false : !s.contains("="));
+		}
+	}
+
+	public boolean startsWithDigit(String str) {
+		if (str == null) {
+			return false;
+		} else if (str.length() < 1) {
+			return false;
+		} else {
+			char c0 = str.charAt(0);
+			return Character.isDigit(c0);
+		}
+	}
+
+	public Block[] parseBlockPart(String domain, String blockPart) {
+		if (this.startsWithDigit(blockPart)) {
+			int[] aint = this.parseIntList(blockPart);
+
+			if (aint == null) {
+				return null;
+			} else {
+				Block[] ablock1 = new Block[aint.length];
+
+				for (int j = 0; j < aint.length; ++j) {
+					int i = aint[j];
+					Block block1 = Block.getBlockById(i);
+
+					if (block1 == null) {
+						this.warn("Block not found for id: " + i);
+						return null;
+					}
+
+					ablock1[j] = block1;
+				}
+
+				return ablock1;
+			}
+		} else {
+			String s = domain + ":" + blockPart;
+			Block block = Block.getBlockFromName(s);
+
+			if (block == null) {
+				this.warn("Block not found for name: " + s);
+				return null;
+			} else {
+				Block[] ablock = new Block[] { block };
+				return ablock;
+			}
+		}
+	}
+
+	public int[] parseBlockMetadatas(Block block, String[] params) {
+		if (params.length <= 0) {
+			return null;
+		} else {
+			String s = params[0];
+
+			if (this.startsWithDigit(s)) {
+				int[] aint = this.parseIntList(s);
+				return aint;
+			} else {
+				IBlockState iblockstate = block.getDefaultState();
+				Collection collection = iblockstate.getPropertyNames();
+				Map<IProperty, List<Comparable>> map = new HashMap();
+
+				for (int i = 0; i < params.length; ++i) {
+					String s1 = params[i];
+
+					if (s1.length() > 0) {
+						String[] astring = Config.tokenize(s1, "=");
+
+						if (astring.length != 2) {
+							this.warn("Invalid block property: " + s1);
+							return null;
+						}
+
+						String s2 = astring[0];
+						String s3 = astring[1];
+						IProperty iproperty = ConnectedProperties.getProperty(s2, collection);
+
+						if (iproperty == null) {
+							this.warn("Property not found: " + s2 + ", block: " + block);
+							return null;
+						}
+
+						List<Comparable> list = (List) map.get(s2);
+
+						if (list == null) {
+							list = new ArrayList();
+							map.put(iproperty, list);
+						}
+
+						String[] astring1 = Config.tokenize(s3, ",");
+
+						for (int j = 0; j < astring1.length; ++j) {
+							String s4 = astring1[j];
+							Comparable comparable = parsePropertyValue(iproperty, s4);
+
+							if (comparable == null) {
+								this.warn(
+										"Property value not found: " + s4 + ", property: " + s2 + ", block: " + block);
+								return null;
+							}
+
+							list.add(comparable);
+						}
+					}
+				}
+
+				if (map.isEmpty()) {
+					return null;
+				} else {
+					List<Integer> list1 = new ArrayList();
+
+					for (int k = 0; k < 16; ++k) {
+						int l = k;
+
+						try {
+							IBlockState iblockstate1 = this.getStateFromMeta(block, l);
+
+							if (this.matchState(iblockstate1, map)) {
+								list1.add(Integer.valueOf(l));
+							}
+						} catch (IllegalArgumentException var18) {
+							;
+						}
+					}
+
+					if (list1.size() == 16) {
+						return null;
+					} else {
+						int[] aint1 = new int[list1.size()];
+
+						for (int i1 = 0; i1 < aint1.length; ++i1) {
+							aint1[i1] = ((Integer) list1.get(i1)).intValue();
+						}
+
+						return aint1;
+					}
+				}
+			}
+		}
+	}
+
+	private IBlockState getStateFromMeta(Block block, int md) {
+		try {
+			IBlockState iblockstate = block.getStateFromMeta(md);
+
+			if (block == Blocks.double_plant && md > 7) {
+				IBlockState iblockstate1 = block.getStateFromMeta(md & 7);
+				iblockstate = iblockstate.withProperty(BlockDoublePlant.VARIANT,
+						iblockstate1.getValue(BlockDoublePlant.VARIANT));
+			}
+
+			return iblockstate;
+		} catch (IllegalArgumentException var5) {
+			return block.getDefaultState();
+		}
+	}
+
+	public static Comparable parsePropertyValue(IProperty prop, String valStr) {
+		Class oclass = prop.getValueClass();
+		Comparable comparable = parseValue(valStr, oclass);
+
+		if (comparable == null) {
+			Collection collection = prop.getAllowedValues();
+			comparable = getPropertyValue(valStr, collection);
+		}
+
+		return comparable;
+	}
+
+	public static Comparable getPropertyValue(String value, Collection propertyValues) {
+		for (Object comparable0 : propertyValues) {
+			Comparable comparable = (Comparable) comparable0;
+			if (getValueName(comparable).equals(value)) {
+				return comparable;
+			}
+		}
+
+		return null;
+	}
+
+	private static Object getValueName(Comparable obj) {
+		if (obj instanceof IStringSerializable) {
+			IStringSerializable istringserializable = (IStringSerializable) obj;
+			return istringserializable.getName();
+		} else {
+			return obj.toString();
+		}
+	}
+
+	public static Comparable parseValue(String str, Class cls) {
+		if (cls == String.class) {
+			return str;
+		} else if (cls == Boolean.class) {
+			return Boolean.valueOf(str);
+		} else if (cls == Float.class) {
+			return Float.valueOf(str);
+		} else if (cls == Double.class) {
+			return Double.valueOf(str);
+		} else if (cls == Integer.class) {
+			return Integer.valueOf(str);
+		} else {
+			return cls == Long.class ? Long.valueOf(str) : null;
+		}
+	}
+
+	public boolean matchState(IBlockState bs, Map<IProperty, List<Comparable>> mapPropValues) {
+		for (IProperty iproperty : mapPropValues.keySet()) {
+			List<Comparable> list = (List) mapPropValues.get(iproperty);
+			Comparable comparable = bs.getValue(iproperty);
+
+			if (comparable == null) {
+				return false;
+			}
+
+			if (!list.contains(comparable)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	public BiomeGenBase[] parseBiomes(String str) {
+		if (str == null) {
+			return null;
+		} else {
+			str = str.trim();
+			boolean flag = false;
+
+			if (str.startsWith("!")) {
+				flag = true;
+				str = str.substring(1);
+			}
+
+			String[] astring = Config.tokenize(str, " ");
+			List list = new ArrayList();
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+				BiomeGenBase biomegenbase = this.findBiome(s);
+
+				if (biomegenbase == null) {
+					this.warn("Biome not found: " + s);
+				} else {
+					list.add(biomegenbase);
+				}
+			}
+
+			if (flag) {
+				List<BiomeGenBase> list1 = new ArrayList(Arrays.asList(BiomeGenBase.getBiomeGenArray()));
+				list1.removeAll(list);
+				list = list1;
+			}
+
+			BiomeGenBase[] abiomegenbase = (BiomeGenBase[]) ((BiomeGenBase[]) list
+					.toArray(new BiomeGenBase[list.size()]));
+			return abiomegenbase;
+		}
+	}
+
+	public BiomeGenBase findBiome(String biomeName) {
+		biomeName = biomeName.toLowerCase();
+
+		if (biomeName.equals("nether")) {
+			return BiomeGenBase.hell;
+		} else {
+			BiomeGenBase[] abiomegenbase = BiomeGenBase.getBiomeGenArray();
+
+			for (int i = 0; i < abiomegenbase.length; ++i) {
+				BiomeGenBase biomegenbase = abiomegenbase[i];
+
+				if (biomegenbase != null) {
+					String s = biomegenbase.biomeName.replace(" ", "").toLowerCase();
+
+					if (s.equals(biomeName)) {
+						return biomegenbase;
+					}
+				}
+			}
+
+			return null;
+		}
+	}
+
+	public int parseInt(String str, int defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			str = str.trim();
+			int i = Config.parseInt(str, -1);
+
+			if (i < 0) {
+				this.warn("Invalid number: " + str);
+				return defVal;
+			} else {
+				return i;
+			}
+		}
+	}
+
+	public int[] parseIntList(String str) {
+		if (str == null) {
+			return null;
+		} else {
+			List<Integer> list = new ArrayList();
+			String[] astring = Config.tokenize(str, " ,");
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+
+				if (s.contains("-")) {
+					String[] astring1 = Config.tokenize(s, "-");
+
+					if (astring1.length != 2) {
+						this.warn("Invalid interval: " + s + ", when parsing: " + str);
+					} else {
+						int k = Config.parseInt(astring1[0], -1);
+						int l = Config.parseInt(astring1[1], -1);
+
+						if (k >= 0 && l >= 0 && k <= l) {
+							for (int i1 = k; i1 <= l; ++i1) {
+								list.add(Integer.valueOf(i1));
+							}
+						} else {
+							this.warn("Invalid interval: " + s + ", when parsing: " + str);
+						}
+					}
+				} else {
+					int j = Config.parseInt(s, -1);
+
+					if (j < 0) {
+						this.warn("Invalid number: " + s + ", when parsing: " + str);
+					} else {
+						list.add(Integer.valueOf(j));
+					}
+				}
+			}
+
+			int[] aint = new int[list.size()];
+
+			for (int j1 = 0; j1 < aint.length; ++j1) {
+				aint[j1] = ((Integer) list.get(j1)).intValue();
+			}
+
+			return aint;
+		}
+	}
+
+	public boolean[] parseFaces(String str, boolean[] defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			EnumSet enumset = EnumSet.allOf(EnumFacing.class);
+			String[] astring = Config.tokenize(str, " ,");
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+
+				if (s.equals("sides")) {
+					enumset.add(EnumFacing.NORTH);
+					enumset.add(EnumFacing.SOUTH);
+					enumset.add(EnumFacing.WEST);
+					enumset.add(EnumFacing.EAST);
+				} else if (s.equals("all")) {
+					enumset.addAll(Arrays.asList(EnumFacing._VALUES));
+				} else {
+					EnumFacing enumfacing = this.parseFace(s);
+
+					if (enumfacing != null) {
+						enumset.add(enumfacing);
+					}
+				}
+			}
+
+			boolean[] aboolean = new boolean[EnumFacing._VALUES.length];
+
+			for (int j = 0; j < aboolean.length; ++j) {
+				aboolean[j] = enumset.contains(EnumFacing._VALUES[j]);
+			}
+
+			return aboolean;
+		}
+	}
+
+	public EnumFacing parseFace(String str) {
+		str = str.toLowerCase();
+
+		if (!str.equals("bottom") && !str.equals("down")) {
+			if (!str.equals("top") && !str.equals("up")) {
+				if (str.equals("north")) {
+					return EnumFacing.NORTH;
+				} else if (str.equals("south")) {
+					return EnumFacing.SOUTH;
+				} else if (str.equals("east")) {
+					return EnumFacing.EAST;
+				} else if (str.equals("west")) {
+					return EnumFacing.WEST;
+				} else {
+					Config.warn("Unknown face: " + str);
+					return null;
+				}
+			} else {
+				return EnumFacing.UP;
+			}
+		} else {
+			return EnumFacing.DOWN;
+		}
+	}
+
+	public void dbg(String str) {
+		Config.dbg("" + this.context + ": " + str);
+	}
+
+	public void warn(String str) {
+		Config.warn("" + this.context + ": " + str);
+	}
+
+	public RangeListInt parseRangeListInt(String str) {
+		if (str == null) {
+			return null;
+		} else {
+			RangeListInt rangelistint = new RangeListInt();
+			String[] astring = Config.tokenize(str, " ,");
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+				RangeInt rangeint = this.parseRangeInt(s);
+
+				if (rangeint == null) {
+					return null;
+				}
+
+				rangelistint.addRange(rangeint);
+			}
+
+			return rangelistint;
+		}
+	}
+
+	private RangeInt parseRangeInt(String str) {
+		if (str == null) {
+			return null;
+		} else if (str.indexOf(45) >= 0) {
+			String[] astring = Config.tokenize(str, "-");
+
+			if (astring.length != 2) {
+				this.warn("Invalid range: " + str);
+				return null;
+			} else {
+				int j = Config.parseInt(astring[0], -1);
+				int k = Config.parseInt(astring[1], -1);
+
+				if (j >= 0 && k >= 0) {
+					return new RangeInt(j, k);
+				} else {
+					this.warn("Invalid range: " + str);
+					return null;
+				}
+			}
+		} else {
+			int i = Config.parseInt(str, -1);
+
+			if (i < 0) {
+				this.warn("Invalid integer: " + str);
+				return null;
+			} else {
+				return new RangeInt(i, i);
+			}
+		}
+	}
+
+	public boolean parseBoolean(String str, boolean defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			String s = str.toLowerCase().trim();
+
+			if (s.equals("true")) {
+				return true;
+			} else if (s.equals("false")) {
+				return false;
+			} else {
+				this.warn("Invalid boolean: " + str);
+				return defVal;
+			}
+		}
+	}
+
+	public Boolean parseBooleanObject(String str) {
+		if (str == null) {
+			return null;
+		} else {
+			String s = str.toLowerCase().trim();
+
+			if (s.equals("true")) {
+				return Boolean.TRUE;
+			} else if (s.equals("false")) {
+				return Boolean.FALSE;
+			} else {
+				this.warn("Invalid boolean: " + str);
+				return null;
+			}
+		}
+	}
+
+	public static int parseColor(String str, int defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			str = str.trim();
+
+			try {
+				int i = Integer.parseInt(str, 16) & 16777215;
+				return i;
+			} catch (NumberFormatException var3) {
+				return defVal;
+			}
+		}
+	}
+
+	public static int parseColor4(String str, int defVal) {
+		if (str == null) {
+			return defVal;
+		} else {
+			str = str.trim();
+
+			try {
+				int i = (int) (Long.parseLong(str, 16) & -1L);
+				return i;
+			} catch (NumberFormatException var3) {
+				return defVal;
+			}
+		}
+	}
+
+	public EnumWorldBlockLayer parseBlockRenderLayer(String str, EnumWorldBlockLayer def) {
+		if (str == null) {
+			return def;
+		} else {
+			str = str.toLowerCase().trim();
+			EnumWorldBlockLayer[] aenumworldblocklayer = EnumWorldBlockLayer.values();
+
+			for (int i = 0; i < aenumworldblocklayer.length; ++i) {
+				EnumWorldBlockLayer enumworldblocklayer = aenumworldblocklayer[i];
+
+				if (str.equals(enumworldblocklayer.name().toLowerCase())) {
+					return enumworldblocklayer;
+				}
+			}
+
+			return def;
+		}
+	}
+
+	public <T> T parseObject(String str, T[] objs, INameGetter nameGetter, String property) {
+		if (str == null) {
+			return (T) null;
+		} else {
+			String s = str.toLowerCase().trim();
+
+			for (int i = 0; i < objs.length; ++i) {
+				T t = objs[i];
+				String s1 = nameGetter.getName(t);
+
+				if (s1 != null && s1.toLowerCase().equals(s)) {
+					return t;
+				}
+			}
+
+			this.warn("Invalid " + property + ": " + str);
+			return (T) null;
+		}
+	}
+
+	public <T> T[] parseObjects(String str, T[] objs, INameGetter nameGetter, String property, T[] errValue) {
+		if (str == null) {
+			return null;
+		} else {
+			str = str.toLowerCase().trim();
+			String[] astring = Config.tokenize(str, " ");
+			T[] at = (T[]) Array.newInstance(objs.getClass().getComponentType(), astring.length);
+
+			for (int i = 0; i < astring.length; ++i) {
+				String s = astring[i];
+				T t = this.parseObject(s, objs, nameGetter, property);
+
+				if (t == null) {
+					return (T[]) errValue;
+				}
+
+				at[i] = t;
+			}
+
+			return at;
+		}
+	}
+
+	public Enum parseEnum(String str, Enum[] enums, String property) {
+		return (Enum) this.parseObject(str, enums, NAME_GETTER_ENUM, property);
+	}
+
+	public Enum[] parseEnums(String str, Enum[] enums, String property, Enum[] errValue) {
+		return (Enum[]) this.parseObjects(str, enums, NAME_GETTER_ENUM, property, errValue);
+	}
+
+	public EnumDyeColor[] parseDyeColors(String str, String property, EnumDyeColor[] errValue) {
+		return (EnumDyeColor[]) this.parseObjects(str, EnumDyeColor.values(), NAME_GETTER_DYE_COLOR, property,
+				errValue);
+	}
+
+	public Weather[] parseWeather(String str, String property, Weather[] errValue) {
+		return (Weather[]) this.parseObjects(str, Weather.values(), NAME_GETTER_ENUM, property, errValue);
+	}
+
+	public NbtTagValue parseNbtTagValue(String path, String value) {
+		return path != null && value != null ? new NbtTagValue(path, value) : null;
+	}
+
+	private static int parseProfessionId(String str) {
+		int i = Config.parseInt(str, -1);
+		return i >= 0 ? i
+				: (str.equals("farmer") ? 0
+						: (str.equals("librarian") ? 1
+								: (str.equals("priest") ? 2
+										: (str.equals("blacksmith") ? 3
+												: (str.equals("butcher") ? 4 : (str.equals("nitwit") ? 5 : -1))))));
+	}
+
+	private static int[] parseCareerIds(int prof, String str) {
+		Set<Integer> set = new HashSet();
+		String[] astring = Config.tokenize(str, ",");
+
+		for (int i = 0; i < astring.length; ++i) {
+			String s = astring[i];
+			int j = parseCareerId(prof, s);
+
+			if (j < 0) {
+				return null;
+			}
+
+			set.add(Integer.valueOf(j));
+		}
+
+		Integer[] ainteger = (Integer[]) ((Integer[]) set.toArray(new Integer[set.size()]));
+		int[] aint = new int[ainteger.length];
+
+		for (int k = 0; k < aint.length; ++k) {
+			aint[k] = ainteger[k].intValue();
+		}
+
+		return aint;
+	}
+
+	private static int parseCareerId(int prof, String str) {
+		int i = Config.parseInt(str, -1);
+
+		if (i >= 0) {
+			return i;
+		} else {
+			if (prof == 0) {
+				if (str.equals("farmer")) {
+					return 1;
+				}
+
+				if (str.equals("fisherman")) {
+					return 2;
+				}
+
+				if (str.equals("shepherd")) {
+					return 3;
+				}
+
+				if (str.equals("fletcher")) {
+					return 4;
+				}
+			}
+
+			if (prof == 1) {
+				if (str.equals("librarian")) {
+					return 1;
+				}
+
+				if (str.equals("cartographer")) {
+					return 2;
+				}
+			}
+
+			if (prof == 2 && str.equals("cleric")) {
+				return 1;
+			} else {
+				if (prof == 3) {
+					if (str.equals("armor")) {
+						return 1;
+					}
+
+					if (str.equals("weapon")) {
+						return 2;
+					}
+
+					if (str.equals("tool")) {
+						return 3;
+					}
+				}
+
+				if (prof == 4) {
+					if (str.equals("butcher")) {
+						return 1;
+					}
+
+					if (str.equals("leather")) {
+						return 2;
+					}
+				}
+
+				return prof == 5 && str.equals("nitwit") ? 1 : -1;
+			}
+		}
+	}
+
+	public int[] parseItems(String str) {
+		str = str.trim();
+		Set<Integer> set = new TreeSet();
+		String[] astring = Config.tokenize(str, " ");
+
+		for (int i = 0; i < astring.length; ++i) {
+			String s = astring[i];
+			ResourceLocation resourcelocation = new ResourceLocation(s);
+			Item item = (Item) Item.itemRegistry.getObject(resourcelocation);
+
+			if (item == null) {
+				this.warn("Item not found: " + s);
+			} else {
+				int j = Item.getIdFromItem(item);
+
+				if (j < 0) {
+					this.warn("Item has no ID: " + item + ", name: " + s);
+				} else {
+					set.add(Integer.valueOf(j));
+				}
+			}
+		}
+
+		Integer[] ainteger = (Integer[]) ((Integer[]) set.toArray(new Integer[set.size()]));
+		int[] aint = Config.toPrimitive(ainteger);
+		return aint;
+	}
+
+}
diff --git a/sources/main/java/net/optifine/config/GlVersion.java b/sources/main/java/net/optifine/config/GlVersion.java
new file mode 100644
index 00000000..7b2b06ab
--- /dev/null
+++ b/sources/main/java/net/optifine/config/GlVersion.java
@@ -0,0 +1,46 @@
+package net.optifine.config;
+
+public class GlVersion {
+	private int major;
+	private int minor;
+	private int release;
+	private String suffix;
+
+	public GlVersion(int major, int minor) {
+		this(major, minor, 0);
+	}
+
+	public GlVersion(int major, int minor, int release) {
+		this(major, minor, release, (String) null);
+	}
+
+	public GlVersion(int major, int minor, int release, String suffix) {
+		this.major = major;
+		this.minor = minor;
+		this.release = release;
+		this.suffix = suffix;
+	}
+
+	public int getMajor() {
+		return this.major;
+	}
+
+	public int getMinor() {
+		return this.minor;
+	}
+
+	public int getRelease() {
+		return this.release;
+	}
+
+	public int toInt() {
+		return this.minor > 9 ? this.major * 100 + this.minor
+				: (this.release > 9 ? this.major * 100 + this.minor * 10 + 9
+						: this.major * 100 + this.minor * 10 + this.release);
+	}
+
+	public String toString() {
+		return this.suffix == null ? "" + this.major + "." + this.minor + "." + this.release
+				: "" + this.major + "." + this.minor + "." + this.release + this.suffix;
+	}
+}
diff --git a/sources/main/java/net/optifine/config/INameGetter.java b/sources/main/java/net/optifine/config/INameGetter.java
new file mode 100644
index 00000000..361d6e4e
--- /dev/null
+++ b/sources/main/java/net/optifine/config/INameGetter.java
@@ -0,0 +1,5 @@
+package net.optifine.config;
+
+public interface INameGetter<T> {
+	String getName(T var1);
+}
diff --git a/sources/main/java/net/optifine/config/IObjectLocator.java b/sources/main/java/net/optifine/config/IObjectLocator.java
new file mode 100644
index 00000000..bdcc511d
--- /dev/null
+++ b/sources/main/java/net/optifine/config/IObjectLocator.java
@@ -0,0 +1,7 @@
+package net.optifine.config;
+
+import net.minecraft.util.ResourceLocation;
+
+public interface IObjectLocator {
+	Object getObject(ResourceLocation var1);
+}
diff --git a/sources/main/java/net/optifine/config/IParserInt.java b/sources/main/java/net/optifine/config/IParserInt.java
new file mode 100644
index 00000000..a4a06434
--- /dev/null
+++ b/sources/main/java/net/optifine/config/IParserInt.java
@@ -0,0 +1,5 @@
+package net.optifine.config;
+
+public interface IParserInt {
+	int parse(String var1, int var2);
+}
diff --git a/sources/main/java/net/optifine/config/ItemLocator.java b/sources/main/java/net/optifine/config/ItemLocator.java
new file mode 100644
index 00000000..92c0eebb
--- /dev/null
+++ b/sources/main/java/net/optifine/config/ItemLocator.java
@@ -0,0 +1,11 @@
+package net.optifine.config;
+
+import net.minecraft.item.Item;
+import net.minecraft.util.ResourceLocation;
+
+public class ItemLocator implements IObjectLocator {
+	public Object getObject(ResourceLocation loc) {
+		Item item = Item.getByNameOrId(loc.toString());
+		return item;
+	}
+}
diff --git a/sources/main/java/net/optifine/config/MatchBlock.java b/sources/main/java/net/optifine/config/MatchBlock.java
new file mode 100644
index 00000000..ad0f2d63
--- /dev/null
+++ b/sources/main/java/net/optifine/config/MatchBlock.java
@@ -0,0 +1,61 @@
+package net.optifine.config;
+
+import net.minecraft.block.state.BlockStateBase;
+import net.optifine.Config;
+
+public class MatchBlock {
+	private int blockId = -1;
+	private int[] metadatas = null;
+
+	public MatchBlock(int blockId) {
+		this.blockId = blockId;
+	}
+
+	public MatchBlock(int blockId, int metadata) {
+		this.blockId = blockId;
+
+		if (metadata >= 0 && metadata <= 15) {
+			this.metadatas = new int[] { metadata };
+		}
+	}
+
+	public MatchBlock(int blockId, int[] metadatas) {
+		this.blockId = blockId;
+		this.metadatas = metadatas;
+	}
+
+	public int getBlockId() {
+		return this.blockId;
+	}
+
+	public int[] getMetadatas() {
+		return this.metadatas;
+	}
+
+	public boolean matches(BlockStateBase blockState) {
+		return blockState.getBlockId() != this.blockId ? false
+				: Matches.metadata(blockState.getMetadata(), this.metadatas);
+	}
+
+	public boolean matches(int id, int metadata) {
+		return id != this.blockId ? false : Matches.metadata(metadata, this.metadatas);
+	}
+
+	public void addMetadata(int metadata) {
+		if (this.metadatas != null) {
+			if (metadata >= 0 && metadata <= 15) {
+				for (int i = 0; i < this.metadatas.length; ++i) {
+					if (this.metadatas[i] == metadata) {
+						return;
+					}
+				}
+
+				this.metadatas = Config.addIntToArray(this.metadatas, metadata);
+			}
+		}
+	}
+
+	public String toString() {
+		return "" + this.blockId + ":" + Config.arrayToString(this.metadatas);
+	}
+}
diff --git a/sources/main/java/net/optifine/config/Matches.java b/sources/main/java/net/optifine/config/Matches.java
new file mode 100644
index 00000000..791c43f9
--- /dev/null
+++ b/sources/main/java/net/optifine/config/Matches.java
@@ -0,0 +1,97 @@
+package net.optifine.config;
+
+import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
+import net.minecraft.block.state.BlockStateBase;
+import net.minecraft.world.biome.BiomeGenBase;
+
+public class Matches {
+	public static boolean block(BlockStateBase blockStateBase, MatchBlock[] matchBlocks) {
+		if (matchBlocks == null) {
+			return true;
+		} else {
+			for (int i = 0; i < matchBlocks.length; ++i) {
+				MatchBlock matchblock = matchBlocks[i];
+
+				if (matchblock.matches(blockStateBase)) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+
+	public static boolean block(int blockId, int metadata, MatchBlock[] matchBlocks) {
+		if (matchBlocks == null) {
+			return true;
+		} else {
+			for (int i = 0; i < matchBlocks.length; ++i) {
+				MatchBlock matchblock = matchBlocks[i];
+
+				if (matchblock.matches(blockId, metadata)) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+
+	public static boolean blockId(int blockId, MatchBlock[] matchBlocks) {
+		if (matchBlocks == null) {
+			return true;
+		} else {
+			for (int i = 0; i < matchBlocks.length; ++i) {
+				MatchBlock matchblock = matchBlocks[i];
+
+				if (matchblock.getBlockId() == blockId) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+
+	public static boolean metadata(int metadata, int[] metadatas) {
+		if (metadatas == null) {
+			return true;
+		} else {
+			for (int i = 0; i < metadatas.length; ++i) {
+				if (metadatas[i] == metadata) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+
+	public static boolean sprite(EaglerTextureAtlasSprite sprite, EaglerTextureAtlasSprite[] sprites) {
+		if (sprites == null) {
+			return true;
+		} else {
+			for (int i = 0; i < sprites.length; ++i) {
+				if (sprites[i] == sprite) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+
+	public static boolean biome(BiomeGenBase biome, BiomeGenBase[] biomes) {
+		if (biomes == null) {
+			return true;
+		} else {
+			for (int i = 0; i < biomes.length; ++i) {
+				if (biomes[i] == biome) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/config/NbtTagValue.java b/sources/main/java/net/optifine/config/NbtTagValue.java
new file mode 100644
index 00000000..3e6a0cb0
--- /dev/null
+++ b/sources/main/java/net/optifine/config/NbtTagValue.java
@@ -0,0 +1,254 @@
+package net.optifine.config;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagByte;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagDouble;
+import net.minecraft.nbt.NBTTagFloat;
+import net.minecraft.nbt.NBTTagInt;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.nbt.NBTTagLong;
+import net.minecraft.nbt.NBTTagShort;
+import net.minecraft.nbt.NBTTagString;
+import net.optifine.Config;
+import net.optifine.util.StrUtils;
+
+public class NbtTagValue {
+	private String[] parents = null;
+	private String name = null;
+	private boolean negative = false;
+	private int type = 0;
+	private String value = null;
+	private int valueFormat = 0;
+	private static final int TYPE_TEXT = 0;
+	private static final int TYPE_PATTERN = 1;
+	private static final int TYPE_IPATTERN = 2;
+	private static final int TYPE_REGEX = 3;
+	private static final int TYPE_IREGEX = 4;
+	private static final String PREFIX_PATTERN = "pattern:";
+	private static final String PREFIX_IPATTERN = "ipattern:";
+	private static final String PREFIX_REGEX = "regex:";
+	private static final String PREFIX_IREGEX = "iregex:";
+	private static final int FORMAT_DEFAULT = 0;
+	private static final int FORMAT_HEX_COLOR = 1;
+	private static final String PREFIX_HEX_COLOR = "#";
+	private static final Pattern PATTERN_HEX_COLOR = Pattern.compile("^#[0-9a-f]{6}+$");
+
+	public NbtTagValue(String tag, String value) {
+		String[] astring = Config.tokenize(tag, ".");
+		this.parents = (String[]) Arrays.copyOfRange(astring, 0, astring.length - 1);
+		this.name = astring[astring.length - 1];
+
+		if (value.startsWith("!")) {
+			this.negative = true;
+			value = value.substring(1);
+		}
+
+		if (value.startsWith("pattern:")) {
+			this.type = 1;
+			value = value.substring("pattern:".length());
+		} else if (value.startsWith("ipattern:")) {
+			this.type = 2;
+			value = value.substring("ipattern:".length()).toLowerCase();
+		} else if (value.startsWith("regex:")) {
+			this.type = 3;
+			value = value.substring("regex:".length());
+		} else if (value.startsWith("iregex:")) {
+			this.type = 4;
+			value = value.substring("iregex:".length()).toLowerCase();
+		} else {
+			this.type = 0;
+		}
+
+		value = StringEscapeUtils.unescapeJava(value);
+
+		if (this.type == 0 && PATTERN_HEX_COLOR.matcher(value).matches()) {
+			this.valueFormat = 1;
+		}
+
+		this.value = value;
+	}
+
+	public boolean matches(NBTTagCompound nbt) {
+		return this.negative ? !this.matchesCompound(nbt) : this.matchesCompound(nbt);
+	}
+
+	public boolean matchesCompound(NBTTagCompound nbt) {
+		if (nbt == null) {
+			return false;
+		} else {
+			NBTBase nbtbase = nbt;
+
+			for (int i = 0; i < this.parents.length; ++i) {
+				String s = this.parents[i];
+				nbtbase = getChildTag(nbtbase, s);
+
+				if (nbtbase == null) {
+					return false;
+				}
+			}
+
+			if (this.name.equals("*")) {
+				return this.matchesAnyChild(nbtbase);
+			} else {
+				nbtbase = getChildTag(nbtbase, this.name);
+
+				if (nbtbase == null) {
+					return false;
+				} else if (this.matchesBase(nbtbase)) {
+					return true;
+				} else {
+					return false;
+				}
+			}
+		}
+	}
+
+	private boolean matchesAnyChild(NBTBase tagBase) {
+		if (tagBase instanceof NBTTagCompound) {
+			NBTTagCompound nbttagcompound = (NBTTagCompound) tagBase;
+
+			for (String s : nbttagcompound.getKeySet()) {
+				NBTBase nbtbase = nbttagcompound.getTag(s);
+
+				if (this.matchesBase(nbtbase)) {
+					return true;
+				}
+			}
+		}
+
+		if (tagBase instanceof NBTTagList) {
+			NBTTagList nbttaglist = (NBTTagList) tagBase;
+			int i = nbttaglist.tagCount();
+
+			for (int j = 0; j < i; ++j) {
+				NBTBase nbtbase1 = nbttaglist.get(j);
+
+				if (this.matchesBase(nbtbase1)) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+	private static NBTBase getChildTag(NBTBase tagBase, String tag) {
+		if (tagBase instanceof NBTTagCompound) {
+			NBTTagCompound nbttagcompound = (NBTTagCompound) tagBase;
+			return nbttagcompound.getTag(tag);
+		} else if (tagBase instanceof NBTTagList) {
+			NBTTagList nbttaglist = (NBTTagList) tagBase;
+
+			if (tag.equals("count")) {
+				return new NBTTagInt(nbttaglist.tagCount());
+			} else {
+				int i = Config.parseInt(tag, -1);
+				return i >= 0 && i < nbttaglist.tagCount() ? nbttaglist.get(i) : null;
+			}
+		} else {
+			return null;
+		}
+	}
+
+	public boolean matchesBase(NBTBase nbtBase) {
+		if (nbtBase == null) {
+			return false;
+		} else {
+			String s = getNbtString(nbtBase, this.valueFormat);
+			return this.matchesValue(s);
+		}
+	}
+
+	public boolean matchesValue(String nbtValue) {
+		if (nbtValue == null) {
+			return false;
+		} else {
+			switch (this.type) {
+			case 0:
+				return nbtValue.equals(this.value);
+
+			case 1:
+				return this.matchesPattern(nbtValue, this.value);
+
+			case 2:
+				return this.matchesPattern(nbtValue.toLowerCase(), this.value);
+
+			case 3:
+				return this.matchesRegex(nbtValue, this.value);
+
+			case 4:
+				return this.matchesRegex(nbtValue.toLowerCase(), this.value);
+
+			default:
+				throw new IllegalArgumentException("Unknown NbtTagValue type: " + this.type);
+			}
+		}
+	}
+
+	private boolean matchesPattern(String str, String pattern) {
+		return StrUtils.equalsMask(str, pattern, '*', '?');
+	}
+
+	private boolean matchesRegex(String str, String regex) {
+		return str.matches(regex);
+	}
+
+	private static String getNbtString(NBTBase nbtBase, int format) {
+		if (nbtBase == null) {
+			return null;
+		} else if (nbtBase instanceof NBTTagString) {
+			NBTTagString nbttagstring = (NBTTagString) nbtBase;
+			return nbttagstring.getString();
+		} else if (nbtBase instanceof NBTTagInt) {
+			NBTTagInt nbttagint = (NBTTagInt) nbtBase;
+			return format == 1 ? "#" + StrUtils.fillLeft(Integer.toHexString(nbttagint.getInt()), 6, '0')
+					: Integer.toString(nbttagint.getInt());
+		} else if (nbtBase instanceof NBTTagByte) {
+			NBTTagByte nbttagbyte = (NBTTagByte) nbtBase;
+			return Byte.toString(nbttagbyte.getByte());
+		} else if (nbtBase instanceof NBTTagShort) {
+			NBTTagShort nbttagshort = (NBTTagShort) nbtBase;
+			return Short.toString(nbttagshort.getShort());
+		} else if (nbtBase instanceof NBTTagLong) {
+			NBTTagLong nbttaglong = (NBTTagLong) nbtBase;
+			return Long.toString(nbttaglong.getLong());
+		} else if (nbtBase instanceof NBTTagFloat) {
+			NBTTagFloat nbttagfloat = (NBTTagFloat) nbtBase;
+			return Float.toString(nbttagfloat.getFloat());
+		} else if (nbtBase instanceof NBTTagDouble) {
+			NBTTagDouble nbttagdouble = (NBTTagDouble) nbtBase;
+			return Double.toString(nbttagdouble.getDouble());
+		} else {
+			return nbtBase.toString();
+		}
+	}
+
+	public String toString() {
+		StringBuffer stringbuffer = new StringBuffer();
+
+		for (int i = 0; i < this.parents.length; ++i) {
+			String s = this.parents[i];
+
+			if (i > 0) {
+				stringbuffer.append(".");
+			}
+
+			stringbuffer.append(s);
+		}
+
+		if (stringbuffer.length() > 0) {
+			stringbuffer.append(".");
+		}
+
+		stringbuffer.append(this.name);
+		stringbuffer.append(" = ");
+		stringbuffer.append(this.value);
+		return stringbuffer.toString();
+	}
+}
diff --git a/sources/main/java/net/optifine/config/ParserEnchantmentId.java b/sources/main/java/net/optifine/config/ParserEnchantmentId.java
new file mode 100644
index 00000000..a523331b
--- /dev/null
+++ b/sources/main/java/net/optifine/config/ParserEnchantmentId.java
@@ -0,0 +1,10 @@
+package net.optifine.config;
+
+import net.minecraft.enchantment.Enchantment;
+
+public class ParserEnchantmentId implements IParserInt {
+	public int parse(String str, int defVal) {
+		Enchantment enchantment = Enchantment.getEnchantmentByLocation(str);
+		return enchantment == null ? defVal : enchantment.effectId;
+	}
+}
diff --git a/sources/main/java/net/optifine/config/RangeInt.java b/sources/main/java/net/optifine/config/RangeInt.java
new file mode 100644
index 00000000..c544644a
--- /dev/null
+++ b/sources/main/java/net/optifine/config/RangeInt.java
@@ -0,0 +1,27 @@
+package net.optifine.config;
+
+public class RangeInt {
+	private int min;
+	private int max;
+
+	public RangeInt(int min, int max) {
+		this.min = Math.min(min, max);
+		this.max = Math.max(min, max);
+	}
+
+	public boolean isInRange(int val) {
+		return val < this.min ? false : val <= this.max;
+	}
+
+	public int getMin() {
+		return this.min;
+	}
+
+	public int getMax() {
+		return this.max;
+	}
+
+	public String toString() {
+		return "min: " + this.min + ", max: " + this.max;
+	}
+}
diff --git a/sources/main/java/net/optifine/config/RangeListInt.java b/sources/main/java/net/optifine/config/RangeListInt.java
new file mode 100644
index 00000000..1fed803e
--- /dev/null
+++ b/sources/main/java/net/optifine/config/RangeListInt.java
@@ -0,0 +1,57 @@
+package net.optifine.config;
+
+public class RangeListInt {
+	private RangeInt[] ranges = new RangeInt[0];
+
+	public RangeListInt() {
+	}
+
+	public RangeListInt(RangeInt ri) {
+		this.addRange(ri);
+	}
+
+	public void addRange(RangeInt ri) {
+		RangeInt[] newRanges = new RangeInt[ranges.length + 1];
+		System.arraycopy(ranges, 0, newRanges, 0, ranges.length);
+		newRanges[ranges.length] = ri;
+		this.ranges = newRanges;
+	}
+
+	public boolean isInRange(int val) {
+		for (int i = 0; i < this.ranges.length; ++i) {
+			RangeInt rangeint = this.ranges[i];
+
+			if (rangeint.isInRange(val)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public int getCountRanges() {
+		return this.ranges.length;
+	}
+
+	public RangeInt getRange(int i) {
+		return this.ranges[i];
+	}
+
+	public String toString() {
+		StringBuffer stringbuffer = new StringBuffer();
+		stringbuffer.append("[");
+
+		for (int i = 0; i < this.ranges.length; ++i) {
+			RangeInt rangeint = this.ranges[i];
+
+			if (i > 0) {
+				stringbuffer.append(", ");
+			}
+
+			stringbuffer.append(rangeint.toString());
+		}
+
+		stringbuffer.append("]");
+		return stringbuffer.toString();
+	}
+}
diff --git a/sources/main/java/net/optifine/config/Weather.java b/sources/main/java/net/optifine/config/Weather.java
new file mode 100644
index 00000000..376bf70e
--- /dev/null
+++ b/sources/main/java/net/optifine/config/Weather.java
@@ -0,0 +1,18 @@
+package net.optifine.config;
+
+import net.minecraft.world.World;
+
+public enum Weather {
+	CLEAR, RAIN, THUNDER;
+
+	public static Weather getWeather(World world, float partialTicks) {
+		float f = world.getThunderStrength(partialTicks);
+
+		if (f > 0.5F) {
+			return THUNDER;
+		} else {
+			float f1 = world.getRainStrength(partialTicks);
+			return f1 > 0.5F ? RAIN : CLEAR;
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/model/BlockModelCustomizer.java b/sources/main/java/net/optifine/model/BlockModelCustomizer.java
new file mode 100644
index 00000000..945c0543
--- /dev/null
+++ b/sources/main/java/net/optifine/model/BlockModelCustomizer.java
@@ -0,0 +1,100 @@
+package net.optifine.model;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumWorldBlockLayer;
+import net.minecraft.world.IBlockAccess;
+import net.optifine.BetterGrass;
+import net.optifine.Config;
+import net.optifine.ConnectedTextures;
+import net.optifine.SmartLeaves;
+import net.optifine.render.RenderEnv;
+
+public class BlockModelCustomizer {
+	private static final List<BakedQuad> NO_QUADS = ImmutableList.<BakedQuad>of();
+
+	public static IBakedModel getRenderModel(IBakedModel modelIn, IBlockState stateIn, RenderEnv renderEnv) {
+		if (renderEnv.isSmartLeaves()) {
+			modelIn = SmartLeaves.getLeavesModel(modelIn, stateIn);
+		}
+
+		return modelIn;
+	}
+
+	public static List<BakedQuad> getRenderQuads(List<BakedQuad> quads, IBlockAccess worldIn, IBlockState stateIn,
+			BlockPos posIn, EnumFacing enumfacing, EnumWorldBlockLayer layer, long rand, RenderEnv renderEnv) {
+		if (enumfacing != null) {
+			if (renderEnv.isSmartLeaves()
+					&& SmartLeaves.isSameLeaves(worldIn.getBlockState(posIn.offset(enumfacing)), stateIn)) {
+				return NO_QUADS;
+			}
+
+			if (!renderEnv.isBreakingAnimation(quads) && Config.isBetterGrass()) {
+				quads = BetterGrass.getFaceQuads(worldIn, stateIn, posIn, enumfacing, quads);
+			}
+		}
+
+		List<BakedQuad> list = renderEnv.getListQuadsCustomizer();
+		list.clear();
+
+		for (int i = 0; i < quads.size(); ++i) {
+			BakedQuad bakedquad = (BakedQuad) quads.get(i);
+			BakedQuad[] abakedquad = getRenderQuads(bakedquad, worldIn, stateIn, posIn, enumfacing, rand, renderEnv);
+
+			if (i == 0 && quads.size() == 1 && abakedquad.length == 1 && abakedquad[0] == bakedquad
+			/* && bakedquad.getQuadEmissive() == null */) {
+				return quads;
+			}
+
+			for (int j = 0; j < abakedquad.length; ++j) {
+				BakedQuad bakedquad1 = abakedquad[j];
+				list.add(bakedquad1);
+
+//				if (bakedquad1.getQuadEmissive() != null) {
+//					renderEnv.getListQuadsOverlay(getEmissiveLayer(layer)).addQuad(bakedquad1.getQuadEmissive(),
+//							stateIn);
+//					renderEnv.setOverlaysRendered(true);
+//				}
+			}
+		}
+
+		return list;
+	}
+
+	private static EnumWorldBlockLayer getEmissiveLayer(EnumWorldBlockLayer layer) {
+		return layer != null && layer != EnumWorldBlockLayer.SOLID ? layer : EnumWorldBlockLayer.CUTOUT_MIPPED;
+	}
+
+	private static BakedQuad[] getRenderQuads(BakedQuad quad, IBlockAccess worldIn, IBlockState stateIn, BlockPos posIn,
+			EnumFacing enumfacing, long rand, RenderEnv renderEnv) {
+		if (renderEnv.isBreakingAnimation(quad)) {
+			return renderEnv.getArrayQuadsCtm(quad);
+		} else {
+//			BakedQuad bakedquad = quad;
+
+			if (Config.isConnectedTextures()) {
+				BakedQuad[] abakedquad = ConnectedTextures.getConnectedTexture(worldIn, stateIn, posIn, quad,
+						renderEnv);
+
+				if (abakedquad.length != 1 || abakedquad[0] != quad) {
+					return abakedquad;
+				}
+			}
+
+//			if (Config.isNaturalTextures()) {
+//				quad = NaturalTextures.getNaturalTexture(posIn, quad);
+//
+//				if (quad != bakedquad) {
+//					return renderEnv.getArrayQuadsCtm(quad);
+//				}
+//			}
+
+			return renderEnv.getArrayQuadsCtm(quad);
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/model/BlockModelUtils.java b/sources/main/java/net/optifine/model/BlockModelUtils.java
new file mode 100644
index 00000000..ff0663b9
--- /dev/null
+++ b/sources/main/java/net/optifine/model/BlockModelUtils.java
@@ -0,0 +1,173 @@
+package net.optifine.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite;
+import net.lax1dude.eaglercraft.v1_8.vector.Vector3f;
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.renderer.block.model.BlockFaceUV;
+import net.minecraft.client.renderer.block.model.BlockPartFace;
+import net.minecraft.client.renderer.block.model.BlockPartRotation;
+import net.minecraft.client.renderer.block.model.BreakingFour;
+import net.minecraft.client.renderer.block.model.FaceBakery;
+import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.client.resources.model.ModelManager;
+import net.minecraft.client.resources.model.ModelResourceLocation;
+import net.minecraft.client.resources.model.ModelRotation;
+import net.minecraft.client.resources.model.SimpleBakedModel;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+
+public class BlockModelUtils {
+	private static final float VERTEX_COORD_ACCURACY = 1.0E-6F;
+
+	public static IBakedModel makeModelCube(String spriteName, int tintIndex) {
+		EaglerTextureAtlasSprite textureatlassprite = Minecraft.getMinecraft().getTextureMapBlocks()
+				.getAtlasSprite(spriteName);
+		return makeModelCube(textureatlassprite, tintIndex);
+	}
+
+	public static IBakedModel makeModelCube(EaglerTextureAtlasSprite sprite, int tintIndex) {
+		List list = new ArrayList();
+		EnumFacing[] aenumfacing = EnumFacing._VALUES;
+		List<List<BakedQuad>> list1 = new ArrayList();
+
+		for (int i = 0; i < aenumfacing.length; ++i) {
+			EnumFacing enumfacing = aenumfacing[i];
+			List list2 = new ArrayList();
+			list2.add(makeBakedQuad(enumfacing, sprite, tintIndex));
+			list1.add(list2);
+		}
+
+		IBakedModel ibakedmodel = new SimpleBakedModel(list, list1, true, true, sprite, ItemCameraTransforms.DEFAULT);
+		return ibakedmodel;
+	}
+
+	public static IBakedModel joinModelsCube(IBakedModel modelBase, IBakedModel modelAdd) {
+		List<BakedQuad> list = new ArrayList();
+		list.addAll(modelBase.getGeneralQuads());
+		list.addAll(modelAdd.getGeneralQuads());
+		EnumFacing[] aenumfacing = EnumFacing._VALUES;
+		List list1 = new ArrayList();
+
+		for (int i = 0; i < aenumfacing.length; ++i) {
+			EnumFacing enumfacing = aenumfacing[i];
+			List list2 = new ArrayList();
+			list2.addAll(modelBase.getFaceQuads(enumfacing));
+			list2.addAll(modelAdd.getFaceQuads(enumfacing));
+			list1.add(list2);
+		}
+
+		boolean flag = modelBase.isAmbientOcclusion();
+		boolean flag1 = modelBase.isBuiltInRenderer();
+		EaglerTextureAtlasSprite textureatlassprite = modelBase.getParticleTexture();
+		ItemCameraTransforms itemcameratransforms = modelBase.getItemCameraTransforms();
+		IBakedModel ibakedmodel = new SimpleBakedModel(list, list1, flag, flag1, textureatlassprite,
+				itemcameratransforms);
+		return ibakedmodel;
+	}
+
+	public static BakedQuad makeBakedQuad(EnumFacing facing, EaglerTextureAtlasSprite sprite, int tintIndex) {
+		Vector3f vector3f = new Vector3f(0.0F, 0.0F, 0.0F);
+		Vector3f vector3f1 = new Vector3f(16.0F, 16.0F, 16.0F);
+		BlockFaceUV blockfaceuv = new BlockFaceUV(new float[] { 0.0F, 0.0F, 16.0F, 16.0F }, 0);
+		BlockPartFace blockpartface = new BlockPartFace(facing, tintIndex, "#" + facing.getName(), blockfaceuv);
+		ModelRotation modelrotation = ModelRotation.X0_Y0;
+		BlockPartRotation blockpartrotation = null;
+		boolean flag = false;
+		boolean flag1 = true;
+		FaceBakery facebakery = new FaceBakery();
+		BakedQuad bakedquad = facebakery.makeBakedQuad(vector3f, vector3f1, blockpartface, sprite, facing,
+				modelrotation, blockpartrotation, flag, flag1);
+		return bakedquad;
+	}
+
+	public static IBakedModel makeModel(String modelName, String spriteOldName, String spriteNewName) {
+		TextureMap texturemap = Minecraft.getMinecraft().getTextureMapBlocks();
+		EaglerTextureAtlasSprite textureatlassprite = texturemap.getAtlasSprite(spriteOldName); // getSpriteSafe
+		EaglerTextureAtlasSprite textureatlassprite1 = texturemap.getAtlasSprite(spriteNewName);
+		return makeModel(modelName, textureatlassprite, textureatlassprite1);
+	}
+
+	public static IBakedModel makeModel(String modelName, EaglerTextureAtlasSprite spriteOld,
+			EaglerTextureAtlasSprite spriteNew) {
+		if (spriteOld != null && spriteNew != null) {
+			ModelManager modelmanager = Minecraft.getMinecraft().getModelManager();
+
+			if (modelmanager == null) {
+				return null;
+			} else {
+				ModelResourceLocation modelresourcelocation = new ModelResourceLocation(modelName, "normal");
+				IBakedModel ibakedmodel = modelmanager.getModel(modelresourcelocation);
+
+				if (ibakedmodel != null && ibakedmodel != modelmanager.getMissingModel()) {
+					IBakedModel ibakedmodel1 = ModelUtils.duplicateModel(ibakedmodel);
+					EnumFacing[] aenumfacing = EnumFacing._VALUES;
+
+					for (int i = 0; i < aenumfacing.length; ++i) {
+						EnumFacing enumfacing = aenumfacing[i];
+						List<BakedQuad> list = ibakedmodel1.getFaceQuads(enumfacing);
+						replaceTexture(list, spriteOld, spriteNew);
+					}
+
+					List<BakedQuad> list1 = ibakedmodel1.getGeneralQuads();
+					replaceTexture(list1, spriteOld, spriteNew);
+					return ibakedmodel1;
+				} else {
+					return null;
+				}
+			}
+		} else {
+			return null;
+		}
+	}
+
+	private static void replaceTexture(List<BakedQuad> quads, EaglerTextureAtlasSprite spriteOld,
+			EaglerTextureAtlasSprite spriteNew) {
+		List<BakedQuad> list = new ArrayList();
+
+		for (BakedQuad bakedquad : quads) {
+			if (bakedquad.getSprite() == spriteOld) {
+				bakedquad = new BreakingFour(bakedquad, spriteNew);
+			}
+
+			list.add(bakedquad);
+		}
+
+		quads.clear();
+		quads.addAll(list);
+	}
+
+	public static void snapVertexPosition(Vector3f pos) {
+		pos.setX(snapVertexCoord(pos.getX()));
+		pos.setY(snapVertexCoord(pos.getY()));
+		pos.setZ(snapVertexCoord(pos.getZ()));
+	}
+
+	private static float snapVertexCoord(float x) {
+		return x > -1.0E-6F && x < 1.0E-6F ? 0.0F : (x > 0.999999F && x < 1.000001F ? 1.0F : x);
+	}
+
+	public static AxisAlignedBB getOffsetBoundingBox(AxisAlignedBB aabb, Block.EnumOffsetType offsetType,
+			BlockPos pos) {
+		int i = pos.getX();
+		int j = pos.getZ();
+		long k = (long) (i * 3129871) ^ (long) j * 116129781L;
+		k = k * k * 42317861L + k * 11L;
+		double d0 = ((double) ((float) (k >> 16 & 15L) / 15.0F) - 0.5D) * 0.5D;
+		double d1 = ((double) ((float) (k >> 24 & 15L) / 15.0F) - 0.5D) * 0.5D;
+		double d2 = 0.0D;
+
+		if (offsetType == Block.EnumOffsetType.XYZ) {
+			d2 = ((double) ((float) (k >> 20 & 15L) / 15.0F) - 1.0D) * 0.2D;
+		}
+
+		return aabb.offset(d0, d2, d1);
+	}
+}
diff --git a/sources/main/java/net/optifine/model/ListQuadsOverlay.java b/sources/main/java/net/optifine/model/ListQuadsOverlay.java
new file mode 100644
index 00000000..124b334d
--- /dev/null
+++ b/sources/main/java/net/optifine/model/ListQuadsOverlay.java
@@ -0,0 +1,44 @@
+package net.optifine.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.init.Blocks;
+
+public class ListQuadsOverlay {
+	private List<BakedQuad> listQuads = new ArrayList();
+	private List<IBlockState> listBlockStates = new ArrayList();
+	private List<BakedQuad> listQuadsSingle = Arrays.<BakedQuad>asList(new BakedQuad[1]);
+
+	public void addQuad(BakedQuad quad, IBlockState blockState) {
+		if (quad != null) {
+			this.listQuads.add(quad);
+			this.listBlockStates.add(blockState);
+		}
+	}
+
+	public int size() {
+		return this.listQuads.size();
+	}
+
+	public BakedQuad getQuad(int index) {
+		return (BakedQuad) this.listQuads.get(index);
+	}
+
+	public IBlockState getBlockState(int index) {
+		return index >= 0 && index < this.listBlockStates.size() ? (IBlockState) this.listBlockStates.get(index)
+				: Blocks.air.getDefaultState();
+	}
+
+	public List<BakedQuad> getListQuadsSingle(BakedQuad quad) {
+		this.listQuadsSingle.set(0, quad);
+		return this.listQuadsSingle;
+	}
+
+	public void clear() {
+		this.listQuads.clear();
+		this.listBlockStates.clear();
+	}
+}
diff --git a/sources/main/java/net/optifine/model/ModelUtils.java b/sources/main/java/net/optifine/model/ModelUtils.java
new file mode 100644
index 00000000..da2c6212
--- /dev/null
+++ b/sources/main/java/net/optifine/model/ModelUtils.java
@@ -0,0 +1,94 @@
+package net.optifine.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.resources.model.IBakedModel;
+import net.minecraft.client.resources.model.SimpleBakedModel;
+import net.minecraft.util.EnumFacing;
+
+public class ModelUtils {
+	public static void dbgModel(IBakedModel model) {
+		if (model != null) {
+			// Config.dbg("Model: " + model + ", ao: " + model.isAmbientOcclusion() + ",
+			// gui3d: " + model.isGui3d() + ", builtIn: " + model.isBuiltInRenderer() + ",
+			// particle: " + model.getParticleTexture());
+			EnumFacing[] aenumfacing = EnumFacing._VALUES;
+
+			for (int i = 0; i < aenumfacing.length; ++i) {
+				EnumFacing enumfacing = aenumfacing[i];
+				List list = model.getFaceQuads(enumfacing);
+				dbgQuads(enumfacing.getName(), list, "  ");
+			}
+
+			List list1 = model.getGeneralQuads();
+			dbgQuads("General", list1, "  ");
+		}
+	}
+
+	private static void dbgQuads(String name, List quads, String prefix) {
+		for (Object bakedquad0 : quads) {
+			BakedQuad bakedQuad = (BakedQuad) bakedquad0;
+			dbgQuad(name, bakedQuad, prefix);
+		}
+	}
+
+	public static void dbgQuad(String name, BakedQuad quad, String prefix) {
+		// Config.dbg(prefix + "Quad: " + quad.getClass().getName() + ", type: " + name
+		// + ", face: " + quad.getFace() + ", tint: " + quad.getTintIndex() + ", sprite:
+		// " + quad.getSprite());
+		dbgVertexData(quad.getVertexData(), "  " + prefix);
+	}
+
+	public static void dbgVertexData(int[] vd, String prefix) {
+		int i = vd.length / 4;
+		// Config.dbg(prefix + "Length: " + vd.length + ", step: " + i);
+
+		for (int j = 0; j < 4; ++j) {
+			int k = j * i;
+			float f = Float.intBitsToFloat(vd[k + 0]);
+			float f1 = Float.intBitsToFloat(vd[k + 1]);
+			float f2 = Float.intBitsToFloat(vd[k + 2]);
+			int l = vd[k + 3];
+			float f3 = Float.intBitsToFloat(vd[k + 4]);
+			float f4 = Float.intBitsToFloat(vd[k + 5]);
+			// Config.dbg(prefix + j + " xyz: " + f + "," + f1 + "," + f2 + " col: " + l + "
+			// u,v: " + f3 + "," + f4);
+		}
+	}
+
+	public static IBakedModel duplicateModel(IBakedModel model) {
+		List list = duplicateQuadList(model.getGeneralQuads());
+		EnumFacing[] aenumfacing = EnumFacing._VALUES;
+		List list1 = new ArrayList();
+
+		for (int i = 0; i < aenumfacing.length; ++i) {
+			EnumFacing enumfacing = aenumfacing[i];
+			List list2 = model.getFaceQuads(enumfacing);
+			List list3 = duplicateQuadList(list2);
+			list1.add(list3);
+		}
+
+		SimpleBakedModel simplebakedmodel = new SimpleBakedModel(list, list1, model.isAmbientOcclusion(),
+				model.isGui3d(), model.getParticleTexture(), model.getItemCameraTransforms());
+		return simplebakedmodel;
+	}
+
+	public static List duplicateQuadList(List lists) {
+		List list = new ArrayList();
+
+		for (Object e : lists) {
+			BakedQuad bakedquad = (BakedQuad) e;
+			BakedQuad bakedquad1 = duplicateQuad(bakedquad);
+			list.add(bakedquad1);
+		}
+
+		return list;
+	}
+
+	public static BakedQuad duplicateQuad(BakedQuad quad) {
+		BakedQuad bakedquad = new BakedQuad((int[]) quad.getVertexData().clone(),
+				(int[]) quad.getVertexDataWithNormals().clone(), quad.getTintIndex(), quad.getFace(), quad.getSprite());
+		return bakedquad;
+	}
+}
diff --git a/sources/main/java/net/optifine/model/QuadBounds.java b/sources/main/java/net/optifine/model/QuadBounds.java
new file mode 100644
index 00000000..850989f8
--- /dev/null
+++ b/sources/main/java/net/optifine/model/QuadBounds.java
@@ -0,0 +1,158 @@
+package net.optifine.model;
+
+import net.minecraft.util.EnumFacing;
+
+public class QuadBounds {
+	private float minX = Float.MAX_VALUE;
+	private float minY = Float.MAX_VALUE;
+	private float minZ = Float.MAX_VALUE;
+	private float maxX = -3.4028235E38F;
+	private float maxY = -3.4028235E38F;
+	private float maxZ = -3.4028235E38F;
+
+	public QuadBounds(int[] vertexData) {
+		int i = vertexData.length / 4;
+
+		for (int j = 0; j < 4; ++j) {
+			int k = j * i;
+			float f = Float.intBitsToFloat(vertexData[k + 0]);
+			float f1 = Float.intBitsToFloat(vertexData[k + 1]);
+			float f2 = Float.intBitsToFloat(vertexData[k + 2]);
+
+			if (this.minX > f) {
+				this.minX = f;
+			}
+
+			if (this.minY > f1) {
+				this.minY = f1;
+			}
+
+			if (this.minZ > f2) {
+				this.minZ = f2;
+			}
+
+			if (this.maxX < f) {
+				this.maxX = f;
+			}
+
+			if (this.maxY < f1) {
+				this.maxY = f1;
+			}
+
+			if (this.maxZ < f2) {
+				this.maxZ = f2;
+			}
+		}
+	}
+
+	public float getMinX() {
+		return this.minX;
+	}
+
+	public float getMinY() {
+		return this.minY;
+	}
+
+	public float getMinZ() {
+		return this.minZ;
+	}
+
+	public float getMaxX() {
+		return this.maxX;
+	}
+
+	public float getMaxY() {
+		return this.maxY;
+	}
+
+	public float getMaxZ() {
+		return this.maxZ;
+	}
+
+	public boolean isFaceQuad(EnumFacing face) {
+		float f;
+		float f1;
+		float f2;
+
+		switch (face) {
+		case DOWN:
+			f = this.getMinY();
+			f1 = this.getMaxY();
+			f2 = 0.0F;
+			break;
+
+		case UP:
+			f = this.getMinY();
+			f1 = this.getMaxY();
+			f2 = 1.0F;
+			break;
+
+		case NORTH:
+			f = this.getMinZ();
+			f1 = this.getMaxZ();
+			f2 = 0.0F;
+			break;
+
+		case SOUTH:
+			f = this.getMinZ();
+			f1 = this.getMaxZ();
+			f2 = 1.0F;
+			break;
+
+		case WEST:
+			f = this.getMinX();
+			f1 = this.getMaxX();
+			f2 = 0.0F;
+			break;
+
+		case EAST:
+			f = this.getMinX();
+			f1 = this.getMaxX();
+			f2 = 1.0F;
+			break;
+
+		default:
+			return false;
+		}
+
+		return f == f2 && f1 == f2;
+	}
+
+	public boolean isFullQuad(EnumFacing face) {
+		float f;
+		float f1;
+		float f2;
+		float f3;
+
+		switch (face) {
+		case DOWN:
+		case UP:
+			f = this.getMinX();
+			f1 = this.getMaxX();
+			f2 = this.getMinZ();
+			f3 = this.getMaxZ();
+			break;
+
+		case NORTH:
+		case SOUTH:
+			f = this.getMinX();
+			f1 = this.getMaxX();
+			f2 = this.getMinY();
+			f3 = this.getMaxY();
+			break;
+
+		case WEST:
+		case EAST:
+			f = this.getMinY();
+			f1 = this.getMaxY();
+			f2 = this.getMinZ();
+			f3 = this.getMaxZ();
+			break;
+
+		default:
+			return false;
+		}
+
+		return f == 0.0F && f1 == 1.0F && f2 == 0.0F && f3 == 1.0F;
+	}
+}
diff --git a/sources/main/java/net/optifine/render/Blender.java b/sources/main/java/net/optifine/render/Blender.java
new file mode 100644
index 00000000..29e76293
--- /dev/null
+++ b/sources/main/java/net/optifine/render/Blender.java
@@ -0,0 +1,122 @@
+package net.optifine.render;
+
+import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
+import net.optifine.Config;
+
+public class Blender {
+	public static final int BLEND_ALPHA = 0;
+	public static final int BLEND_ADD = 1;
+	public static final int BLEND_SUBSTRACT = 2;
+	public static final int BLEND_MULTIPLY = 3;
+	public static final int BLEND_DODGE = 4;
+	public static final int BLEND_BURN = 5;
+	public static final int BLEND_SCREEN = 6;
+	public static final int BLEND_OVERLAY = 7;
+	public static final int BLEND_REPLACE = 8;
+	public static final int BLEND_DEFAULT = 1;
+
+	public static int parseBlend(String str) {
+		if (str == null) {
+			return 1;
+		} else {
+			str = str.toLowerCase().trim();
+
+			if (str.equals("alpha")) {
+				return 0;
+			} else if (str.equals("add")) {
+				return 1;
+			} else if (str.equals("subtract")) {
+				return 2;
+			} else if (str.equals("multiply")) {
+				return 3;
+			} else if (str.equals("dodge")) {
+				return 4;
+			} else if (str.equals("burn")) {
+				return 5;
+			} else if (str.equals("screen")) {
+				return 6;
+			} else if (str.equals("overlay")) {
+				return 7;
+			} else if (str.equals("replace")) {
+				return 8;
+			} else {
+				Config.warn("Unknown blend: " + str);
+				return 1;
+			}
+		}
+	}
+
+	public static void setupBlend(int blend, float brightness) {
+		switch (blend) {
+		case 0:
+			GlStateManager.disableAlpha();
+			GlStateManager.enableBlend();
+			GlStateManager.blendFunc(770, 771);
+			GlStateManager.color(1.0F, 1.0F, 1.0F, brightness);
+			break;
+
+		case 1:
+			GlStateManager.disableAlpha();
+			GlStateManager.enableBlend();
+			GlStateManager.blendFunc(770, 1);
+			GlStateManager.color(1.0F, 1.0F, 1.0F, brightness);
+			break;
+
+		case 2:
+			GlStateManager.disableAlpha();
+			GlStateManager.enableBlend();
+			GlStateManager.blendFunc(775, 0);
+			GlStateManager.color(brightness, brightness, brightness, 1.0F);
+			break;
+
+		case 3:
+			GlStateManager.disableAlpha();
+			GlStateManager.enableBlend();
+			GlStateManager.blendFunc(774, 771);
+			GlStateManager.color(brightness, brightness, brightness, brightness);
+			break;
+
+		case 4:
+			GlStateManager.disableAlpha();
+			GlStateManager.enableBlend();
+			GlStateManager.blendFunc(1, 1);
+			GlStateManager.color(brightness, brightness, brightness, 1.0F);
+			break;
+
+		case 5:
+			GlStateManager.disableAlpha();
+			GlStateManager.enableBlend();
+			GlStateManager.blendFunc(0, 769);
+			GlStateManager.color(brightness, brightness, brightness, 1.0F);
+			break;
+
+		case 6:
+			GlStateManager.disableAlpha();
+			GlStateManager.enableBlend();
+			GlStateManager.blendFunc(1, 769);
+			GlStateManager.color(brightness, brightness, brightness, 1.0F);
+			break;
+
+		case 7:
+			GlStateManager.disableAlpha();
+			GlStateManager.enableBlend();
+			GlStateManager.blendFunc(774, 768);
+			GlStateManager.color(brightness, brightness, brightness, 1.0F);
+			break;
+
+		case 8:
+			GlStateManager.enableAlpha();
+			GlStateManager.disableBlend();
+			GlStateManager.color(1.0F, 1.0F, 1.0F, brightness);
+		}
+
+		GlStateManager.enableTexture2D();
+	}
+
+	public static void clearBlend(float rainBrightness) {
+		GlStateManager.disableAlpha();
+		GlStateManager.enableBlend();
+		GlStateManager.blendFunc(770, 1);
+		GlStateManager.color(1.0F, 1.0F, 1.0F, rainBrightness);
+	}
+}
diff --git a/sources/main/java/net/optifine/render/RenderEnv.java b/sources/main/java/net/optifine/render/RenderEnv.java
new file mode 100644
index 00000000..78238def
--- /dev/null
+++ b/sources/main/java/net/optifine/render/RenderEnv.java
@@ -0,0 +1,265 @@
+package net.optifine.render;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockLeaves;
+import net.minecraft.block.state.BlockStateBase;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.renderer.BlockModelRenderer;
+import net.minecraft.client.renderer.RegionRenderCacheBuilder;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.renderer.block.model.BreakingFour;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumWorldBlockLayer;
+import net.optifine.Config;
+import net.optifine.model.ListQuadsOverlay;
+
+public class RenderEnv {
+	private IBlockState blockState;
+	private BlockPos blockPos;
+	private int blockId = -1;
+	private int metadata = -1;
+	private int breakingAnimation = -1;
+	private int smartLeaves = -1;
+	private float[] quadBounds = new float[EnumFacing._VALUES.length * 2];
+	private BitSet boundsFlags = new BitSet(3);
+	private BlockModelRenderer.AmbientOcclusionFace aoFace = new BlockModelRenderer.AmbientOcclusionFace();
+	private BlockPos colorizerBlockPosM = null;
+	private boolean[] borderFlags = null;
+	private boolean[] borderFlags2 = null;
+	private boolean[] borderFlags3 = null;
+	private EnumFacing[] borderDirections = null;
+	private List<BakedQuad> listQuadsCustomizer = new ArrayList();
+	private List<BakedQuad> listQuadsCtmMultipass = new ArrayList();
+	private BakedQuad[] arrayQuadsCtm1 = new BakedQuad[1];
+	private BakedQuad[] arrayQuadsCtm2 = new BakedQuad[2];
+	private BakedQuad[] arrayQuadsCtm3 = new BakedQuad[3];
+	private BakedQuad[] arrayQuadsCtm4 = new BakedQuad[4];
+	private RegionRenderCacheBuilder regionRenderCacheBuilder = null;
+	private ListQuadsOverlay[] listsQuadsOverlay = new ListQuadsOverlay[EnumWorldBlockLayer.values().length];
+	private boolean overlaysRendered = false;
+	private static final int UNKNOWN = -1;
+	private static final int FALSE = 0;
+	private static final int TRUE = 1;
+
+	public RenderEnv(IBlockState blockState, BlockPos blockPos) {
+		this.blockState = blockState;
+		this.blockPos = blockPos;
+	}
+
+	public void reset(IBlockState blockStateIn, BlockPos blockPosIn) {
+		if (this.blockState != blockStateIn || this.blockPos != blockPosIn) {
+			this.blockState = blockStateIn;
+			this.blockPos = blockPosIn;
+			this.blockId = -1;
+			this.metadata = -1;
+			this.breakingAnimation = -1;
+			this.smartLeaves = -1;
+			this.boundsFlags.clear();
+		}
+	}
+
+	public int getBlockId() {
+		if (this.blockId < 0) {
+			if (this.blockState instanceof BlockStateBase) {
+				BlockStateBase blockstatebase = (BlockStateBase) this.blockState;
+				this.blockId = blockstatebase.getBlockId();
+			} else {
+				this.blockId = Block.getIdFromBlock(this.blockState.getBlock());
+			}
+		}
+
+		return this.blockId;
+	}
+
+	public int getMetadata() {
+		if (this.metadata < 0) {
+			if (this.blockState instanceof BlockStateBase) {
+				BlockStateBase blockstatebase = (BlockStateBase) this.blockState;
+				this.metadata = blockstatebase.getMetadata();
+			} else {
+				this.metadata = this.blockState.getBlock().getMetaFromState(this.blockState);
+			}
+		}
+
+		return this.metadata;
+	}
+
+	public float[] getQuadBounds() {
+		return this.quadBounds;
+	}
+
+	public BitSet getBoundsFlags() {
+		return this.boundsFlags;
+	}
+
+	public BlockModelRenderer.AmbientOcclusionFace getAoFace() {
+		return this.aoFace;
+	}
+
+	public boolean isBreakingAnimation(List listQuads) {
+		if (this.breakingAnimation == -1 && listQuads.size() > 0) {
+			if (listQuads.get(0) instanceof BreakingFour) {
+				this.breakingAnimation = 1;
+			} else {
+				this.breakingAnimation = 0;
+			}
+		}
+
+		return this.breakingAnimation == 1;
+	}
+
+	public boolean isBreakingAnimation(BakedQuad quad) {
+		if (this.breakingAnimation < 0) {
+			if (quad instanceof BreakingFour) {
+				this.breakingAnimation = 1;
+			} else {
+				this.breakingAnimation = 0;
+			}
+		}
+
+		return this.breakingAnimation == 1;
+	}
+
+	public boolean isBreakingAnimation() {
+		return this.breakingAnimation == 1;
+	}
+
+	public IBlockState getBlockState() {
+		return this.blockState;
+	}
+
+	public BlockPos getColorizerBlockPosM() {
+		if (this.colorizerBlockPosM == null) {
+			this.colorizerBlockPosM = new BlockPos(0, 0, 0);
+		}
+
+		return this.colorizerBlockPosM;
+	}
+
+	public boolean[] getBorderFlags() {
+		if (this.borderFlags == null) {
+			this.borderFlags = new boolean[4];
+		}
+
+		return this.borderFlags;
+	}
+
+	public boolean[] getBorderFlags2() {
+		if (this.borderFlags2 == null) {
+			this.borderFlags2 = new boolean[4];
+		}
+
+		return this.borderFlags2;
+	}
+
+	public boolean[] getBorderFlags3() {
+		if (this.borderFlags3 == null) {
+			this.borderFlags3 = new boolean[4];
+		}
+
+		return this.borderFlags3;
+	}
+
+	public EnumFacing[] getBorderDirections() {
+		if (this.borderDirections == null) {
+			this.borderDirections = new EnumFacing[4];
+		}
+
+		return this.borderDirections;
+	}
+
+	public EnumFacing[] getBorderDirections(EnumFacing dir0, EnumFacing dir1, EnumFacing dir2, EnumFacing dir3) {
+		EnumFacing[] aenumfacing = this.getBorderDirections();
+		aenumfacing[0] = dir0;
+		aenumfacing[1] = dir1;
+		aenumfacing[2] = dir2;
+		aenumfacing[3] = dir3;
+		return aenumfacing;
+	}
+
+	public boolean isSmartLeaves() {
+		if (this.smartLeaves == -1) {
+			if (Config.isTreesSmart() && this.blockState.getBlock() instanceof BlockLeaves) {
+				this.smartLeaves = 1;
+			} else {
+				this.smartLeaves = 0;
+			}
+		}
+
+		return this.smartLeaves == 1;
+	}
+
+	public List<BakedQuad> getListQuadsCustomizer() {
+		return this.listQuadsCustomizer;
+	}
+
+	public BakedQuad[] getArrayQuadsCtm(BakedQuad quad) {
+		this.arrayQuadsCtm1[0] = quad;
+		return this.arrayQuadsCtm1;
+	}
+
+	public BakedQuad[] getArrayQuadsCtm(BakedQuad quad0, BakedQuad quad1) {
+		this.arrayQuadsCtm2[0] = quad0;
+		this.arrayQuadsCtm2[1] = quad1;
+		return this.arrayQuadsCtm2;
+	}
+
+	public BakedQuad[] getArrayQuadsCtm(BakedQuad quad0, BakedQuad quad1, BakedQuad quad2) {
+		this.arrayQuadsCtm3[0] = quad0;
+		this.arrayQuadsCtm3[1] = quad1;
+		this.arrayQuadsCtm3[2] = quad2;
+		return this.arrayQuadsCtm3;
+	}
+
+	public BakedQuad[] getArrayQuadsCtm(BakedQuad quad0, BakedQuad quad1, BakedQuad quad2, BakedQuad quad3) {
+		this.arrayQuadsCtm4[0] = quad0;
+		this.arrayQuadsCtm4[1] = quad1;
+		this.arrayQuadsCtm4[2] = quad2;
+		this.arrayQuadsCtm4[3] = quad3;
+		return this.arrayQuadsCtm4;
+	}
+
+	public List<BakedQuad> getListQuadsCtmMultipass(BakedQuad[] quads) {
+		this.listQuadsCtmMultipass.clear();
+
+		if (quads != null) {
+			for (int i = 0; i < quads.length; ++i) {
+				BakedQuad bakedquad = quads[i];
+				this.listQuadsCtmMultipass.add(bakedquad);
+			}
+		}
+
+		return this.listQuadsCtmMultipass;
+	}
+
+	public RegionRenderCacheBuilder getRegionRenderCacheBuilder() {
+		return this.regionRenderCacheBuilder;
+	}
+
+	public void setRegionRenderCacheBuilder(RegionRenderCacheBuilder regionRenderCacheBuilder) {
+		this.regionRenderCacheBuilder = regionRenderCacheBuilder;
+	}
+
+	public ListQuadsOverlay getListQuadsOverlay(EnumWorldBlockLayer layer) {
+		ListQuadsOverlay listquadsoverlay = this.listsQuadsOverlay[layer.ordinal()];
+
+		if (listquadsoverlay == null) {
+			listquadsoverlay = new ListQuadsOverlay();
+			this.listsQuadsOverlay[layer.ordinal()] = listquadsoverlay;
+		}
+
+		return listquadsoverlay;
+	}
+
+	public boolean isOverlaysRendered() {
+		return this.overlaysRendered;
+	}
+
+	public void setOverlaysRendered(boolean overlaysRendered) {
+		this.overlaysRendered = overlaysRendered;
+	}
+}
diff --git a/sources/main/java/net/optifine/util/CounterInt.java b/sources/main/java/net/optifine/util/CounterInt.java
new file mode 100644
index 00000000..976aa695
--- /dev/null
+++ b/sources/main/java/net/optifine/util/CounterInt.java
@@ -0,0 +1,24 @@
+package net.optifine.util;
+
+public class CounterInt {
+	private int startValue;
+	private int value;
+
+	public CounterInt(int startValue) {
+		this.startValue = startValue;
+		this.value = startValue;
+	}
+
+	public int nextValue() {
+		int i = this.value++;
+		return i;
+	}
+
+	public void reset() {
+		this.value = this.startValue;
+	}
+
+	public int getValue() {
+		return this.value;
+	}
+}
diff --git a/sources/main/java/net/optifine/util/MathUtils.java b/sources/main/java/net/optifine/util/MathUtils.java
new file mode 100644
index 00000000..f8177687
--- /dev/null
+++ b/sources/main/java/net/optifine/util/MathUtils.java
@@ -0,0 +1,74 @@
+package net.optifine.util;
+
+import net.minecraft.util.MathHelper;
+
+public class MathUtils {
+	public static final float PI = (float) Math.PI;
+	public static final float PI2 = ((float) Math.PI * 2F);
+	public static final float PId2 = ((float) Math.PI / 2F);
+//	private static final float[] ASIN_TABLE = new float[65536];
+//
+//	public static float asin(float value) {
+//		return ASIN_TABLE[(int) ((double) (value + 1.0F) * 32767.5D) & 65535];
+//	}
+//
+//	public static float acos(float value) {
+//		return ((float) Math.PI / 2F) - ASIN_TABLE[(int) ((double) (value + 1.0F) * 32767.5D) & 65535];
+//	}
+
+	public static int getAverage(int[] vals) {
+		if (vals.length <= 0) {
+			return 0;
+		} else {
+			int i = getSum(vals);
+			int j = i / vals.length;
+			return j;
+		}
+	}
+
+	public static int getSum(int[] vals) {
+		if (vals.length <= 0) {
+			return 0;
+		} else {
+			int i = 0;
+
+			for (int j = 0; j < vals.length; ++j) {
+				int k = vals[j];
+				i += k;
+			}
+
+			return i;
+		}
+	}
+
+	public static int roundDownToPowerOfTwo(int val) {
+		int i = MathHelper.roundUpToPowerOfTwo(val);
+		return val == i ? i : i / 2;
+	}
+
+	public static boolean equalsDelta(float f1, float f2, float delta) {
+		return Math.abs(f1 - f2) <= delta;
+	}
+
+	public static float toDeg(float angle) {
+		return angle * 180.0F / PI;
+	}
+
+	public static float toRad(float angle) {
+		return angle / 180.0F * PI;
+	}
+
+	public static float roundToFloat(double d) {
+		return (float) ((double) Math.round(d * 1.0E8D) / 1.0E8D);
+	}
+
+//	static {
+//		for (int i = 0; i < 65536; ++i) {
+//			ASIN_TABLE[i] = (float) Math.asin((double) i / 32767.5D - 1.0D);
+//		}
+//
+//		for (int j = -1; j < 2; ++j) {
+//			ASIN_TABLE[(int) (((double) j + 1.0D) * 32767.5D) & 65535] = (float) Math.asin((double) j);
+//		}
+//	}
+}
diff --git a/sources/main/java/net/optifine/util/NumUtils.java b/sources/main/java/net/optifine/util/NumUtils.java
new file mode 100644
index 00000000..669b5ae2
--- /dev/null
+++ b/sources/main/java/net/optifine/util/NumUtils.java
@@ -0,0 +1,17 @@
+package net.optifine.util;
+
+public class NumUtils {
+	public static float limit(float val, float min, float max) {
+		return val < min ? min : (val > max ? max : val);
+	}
+
+	public static int mod(int x, int y) {
+		int i = x % y;
+
+		if (i < 0) {
+			i += y;
+		}
+
+		return i;
+	}
+}
diff --git a/sources/main/java/net/optifine/util/PropertiesOrdered.java b/sources/main/java/net/optifine/util/PropertiesOrdered.java
new file mode 100644
index 00000000..a8bcf459
--- /dev/null
+++ b/sources/main/java/net/optifine/util/PropertiesOrdered.java
@@ -0,0 +1,27 @@
+package net.optifine.util;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import net.lax1dude.eaglercraft.v1_8.EaglerProperties;
+
+public class PropertiesOrdered extends EaglerProperties {
+	private Set<Object> keysOrdered = new LinkedHashSet();
+
+	public Object put(Object key, Object value) {
+		this.keysOrdered.add(key);
+		return super.put(key, value);
+	}
+
+	public Set<Object> keySet() {
+		Set<Object> set = super.keySet();
+		this.keysOrdered.retainAll(set);
+		return Collections.<Object>unmodifiableSet(this.keysOrdered);
+	}
+
+	public Enumeration<Object> keys() {
+		return Collections.<Object>enumeration(this.keySet());
+	}
+}
diff --git a/sources/main/java/net/optifine/util/ResUtils.java b/sources/main/java/net/optifine/util/ResUtils.java
new file mode 100644
index 00000000..fbcb4c99
--- /dev/null
+++ b/sources/main/java/net/optifine/util/ResUtils.java
@@ -0,0 +1,54 @@
+package net.optifine.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.client.resources.IResourcePack;
+
+public class ResUtils {
+
+	public static List<String> collectPropertyFiles(IResourcePack rp, String prefix) {
+		List<String> ret = new ArrayList<>();
+		for (String str : rp.getEaglerFileIndex().getPropertiesFiles()) {
+			if (str.startsWith(prefix)) {
+				ret.add(str);
+			}
+		}
+		return ret;
+	}
+
+	public static List<String> collectPropertyFiles(IResourcePack rp, String... prefixes) {
+		List<String> ret = new ArrayList<>();
+		for (String str : rp.getEaglerFileIndex().getPropertiesFiles()) {
+			for (int i = 0; i < prefixes.length; ++i) {
+				if (str.startsWith(prefixes[i])) {
+					ret.add(str);
+				}
+			}
+		}
+		return ret;
+	}
+
+	public static List<String> collectPotionFiles(IResourcePack rp, String prefix) {
+		List<String> ret = new ArrayList<>();
+		for (String str : rp.getEaglerFileIndex().getCITPotionsFiles()) {
+			if (str.startsWith(prefix)) {
+				ret.add(str);
+			}
+		}
+		return ret;
+	}
+
+	public static List<String> collectPotionFiles(IResourcePack rp, String... prefixes) {
+		List<String> ret = new ArrayList<>();
+		for (String str : rp.getEaglerFileIndex().getCITPotionsFiles()) {
+			for (int i = 0; i < prefixes.length; ++i) {
+				if (str.startsWith(prefixes[i])) {
+					ret.add(str);
+				}
+			}
+		}
+		return ret;
+	}
+
+}
diff --git a/sources/main/java/net/optifine/util/SmoothFloat.java b/sources/main/java/net/optifine/util/SmoothFloat.java
new file mode 100644
index 00000000..fdfd0b3f
--- /dev/null
+++ b/sources/main/java/net/optifine/util/SmoothFloat.java
@@ -0,0 +1,77 @@
+package net.optifine.util;
+
+public class SmoothFloat {
+	private float valueLast;
+	private float timeFadeUpSec;
+	private float timeFadeDownSec;
+	private long timeLastMs;
+
+	public SmoothFloat(float valueLast, float timeFadeSec) {
+		this(valueLast, timeFadeSec, timeFadeSec);
+	}
+
+	public SmoothFloat(float valueLast, float timeFadeUpSec, float timeFadeDownSec) {
+		this.valueLast = valueLast;
+		this.timeFadeUpSec = timeFadeUpSec;
+		this.timeFadeDownSec = timeFadeDownSec;
+		this.timeLastMs = System.currentTimeMillis();
+	}
+
+	public float getValueLast() {
+		return this.valueLast;
+	}
+
+	public float getTimeFadeUpSec() {
+		return this.timeFadeUpSec;
+	}
+
+	public float getTimeFadeDownSec() {
+		return this.timeFadeDownSec;
+	}
+
+	public long getTimeLastMs() {
+		return this.timeLastMs;
+	}
+
+	public float getSmoothValue(float value, float timeFadeUpSec, float timeFadeDownSec) {
+		this.timeFadeUpSec = timeFadeUpSec;
+		this.timeFadeDownSec = timeFadeDownSec;
+		return this.getSmoothValue(value);
+	}
+
+	public float getSmoothValue(float value) {
+		long i = System.currentTimeMillis();
+		float f = this.valueLast;
+		long j = this.timeLastMs;
+		float f1 = (float) (i - j) / 1000.0F;
+		float f2 = value >= f ? this.timeFadeUpSec : this.timeFadeDownSec;
+		float f3 = getSmoothValue(f, value, f1, f2);
+		this.valueLast = f3;
+		this.timeLastMs = i;
+		return f3;
+	}
+
+	public static float getSmoothValue(float valPrev, float value, float timeDeltaSec, float timeFadeSec) {
+		if (timeDeltaSec <= 0.0F) {
+			return valPrev;
+		} else {
+			float f = value - valPrev;
+			float f1;
+
+			if (timeFadeSec > 0.0F && timeDeltaSec < timeFadeSec && Math.abs(f) > 1.0E-6F) {
+				float f2 = timeFadeSec / timeDeltaSec;
+				float f3 = 4.61F;
+				float f4 = 0.13F;
+				float f5 = 10.0F;
+				float f6 = f3 - 1.0F / (f4 + f2 / f5);
+				float f7 = timeDeltaSec / timeFadeSec * f6;
+				f7 = NumUtils.limit(f7, 0.0F, 1.0F);
+				f1 = valPrev + f * f7;
+			} else {
+				f1 = value;
+			}
+
+			return f1;
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/util/StrUtils.java b/sources/main/java/net/optifine/util/StrUtils.java
new file mode 100644
index 00000000..c1e49aef
--- /dev/null
+++ b/sources/main/java/net/optifine/util/StrUtils.java
@@ -0,0 +1,603 @@
+package net.optifine.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class StrUtils {
+	public static boolean equalsMask(String str, String mask, char wildChar, char wildCharSingle) {
+		if (mask != null && str != null) {
+			if (mask.indexOf(wildChar) < 0) {
+				return mask.indexOf(wildCharSingle) < 0 ? mask.equals(str)
+						: equalsMaskSingle(str, mask, wildCharSingle);
+			} else {
+				List list = new ArrayList();
+				String s = "" + wildChar;
+
+				if (mask.startsWith(s)) {
+					list.add("");
+				}
+
+				StringTokenizer stringtokenizer = new StringTokenizer(mask, s);
+
+				while (stringtokenizer.hasMoreElements()) {
+					list.add(stringtokenizer.nextToken());
+				}
+
+				if (mask.endsWith(s)) {
+					list.add("");
+				}
+
+				String s1 = (String) list.get(0);
+
+				if (!startsWithMaskSingle(str, s1, wildCharSingle)) {
+					return false;
+				} else {
+					String s2 = (String) list.get(list.size() - 1);
+
+					if (!endsWithMaskSingle(str, s2, wildCharSingle)) {
+						return false;
+					} else {
+						int i = 0;
+
+						for (int j = 0; j < ((List) list).size(); ++j) {
+							String s3 = (String) list.get(j);
+
+							if (s3.length() > 0) {
+								int k = indexOfMaskSingle(str, s3, i, wildCharSingle);
+
+								if (k < 0) {
+									return false;
+								}
+
+								i = k + s3.length();
+							}
+						}
+
+						return true;
+					}
+				}
+			}
+		} else {
+			return mask == str;
+		}
+	}
+
+	private static boolean equalsMaskSingle(String str, String mask, char wildCharSingle) {
+		if (str != null && mask != null) {
+			if (str.length() != mask.length()) {
+				return false;
+			} else {
+				for (int i = 0; i < mask.length(); ++i) {
+					char c0 = mask.charAt(i);
+
+					if (c0 != wildCharSingle && str.charAt(i) != c0) {
+						return false;
+					}
+				}
+
+				return true;
+			}
+		} else {
+			return str == mask;
+		}
+	}
+
+	private static int indexOfMaskSingle(String str, String mask, int startPos, char wildCharSingle) {
+		if (str != null && mask != null) {
+			if (startPos >= 0 && startPos <= str.length()) {
+				if (str.length() < startPos + mask.length()) {
+					return -1;
+				} else {
+					for (int i = startPos; i + mask.length() <= str.length(); ++i) {
+						String s = str.substring(i, i + mask.length());
+
+						if (equalsMaskSingle(s, mask, wildCharSingle)) {
+							return i;
+						}
+					}
+
+					return -1;
+				}
+			} else {
+				return -1;
+			}
+		} else {
+			return -1;
+		}
+	}
+
+	private static boolean endsWithMaskSingle(String str, String mask, char wildCharSingle) {
+		if (str != null && mask != null) {
+			if (str.length() < mask.length()) {
+				return false;
+			} else {
+				String s = str.substring(str.length() - mask.length(), str.length());
+				return equalsMaskSingle(s, mask, wildCharSingle);
+			}
+		} else {
+			return str == mask;
+		}
+	}
+
+	private static boolean startsWithMaskSingle(String str, String mask, char wildCharSingle) {
+		if (str != null && mask != null) {
+			if (str.length() < mask.length()) {
+				return false;
+			} else {
+				String s = str.substring(0, mask.length());
+				return equalsMaskSingle(s, mask, wildCharSingle);
+			}
+		} else {
+			return str == mask;
+		}
+	}
+
+	public static boolean equalsMask(String str, String[] masks, char wildChar) {
+		for (int i = 0; i < masks.length; ++i) {
+			String s = masks[i];
+
+			if (equalsMask(str, s, wildChar)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public static boolean equalsMask(String str, String mask, char wildChar) {
+		if (mask != null && str != null) {
+			if (mask.indexOf(wildChar) < 0) {
+				return mask.equals(str);
+			} else {
+				List list = new ArrayList();
+				String s = "" + wildChar;
+
+				if (mask.startsWith(s)) {
+					list.add("");
+				}
+
+				StringTokenizer stringtokenizer = new StringTokenizer(mask, s);
+
+				while (stringtokenizer.hasMoreElements()) {
+					list.add(stringtokenizer.nextToken());
+				}
+
+				if (mask.endsWith(s)) {
+					list.add("");
+				}
+
+				String s1 = (String) list.get(0);
+
+				if (!str.startsWith(s1)) {
+					return false;
+				} else {
+					String s2 = (String) list.get(list.size() - 1);
+
+					if (!str.endsWith(s2)) {
+						return false;
+					} else {
+						int i = 0;
+
+						for (int j = 0; j < ((List) list).size(); ++j) {
+							String s3 = (String) list.get(j);
+
+							if (s3.length() > 0) {
+								int k = str.indexOf(s3, i);
+
+								if (k < 0) {
+									return false;
+								}
+
+								i = k + s3.length();
+							}
+						}
+
+						return true;
+					}
+				}
+			}
+		} else {
+			return mask == str;
+		}
+	}
+
+	public static String[] split(String str, String separators) {
+		if (str != null && str.length() > 0) {
+			if (separators == null) {
+				return new String[] { str };
+			} else {
+				List list = new ArrayList();
+				int i = 0;
+
+				for (int j = 0; j < str.length(); ++j) {
+					char c0 = str.charAt(j);
+
+					if (equals(c0, separators)) {
+						list.add(str.substring(i, j));
+						i = j + 1;
+					}
+				}
+
+				list.add(str.substring(i, str.length()));
+				return (String[]) ((String[]) list.toArray(new String[list.size()]));
+			}
+		} else {
+			return new String[0];
+		}
+	}
+
+	private static boolean equals(char ch, String matches) {
+		for (int i = 0; i < matches.length(); ++i) {
+			if (matches.charAt(i) == ch) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public static boolean equalsTrim(String a, String b) {
+		if (a != null) {
+			a = a.trim();
+		}
+
+		if (b != null) {
+			b = b.trim();
+		}
+
+		return equals(a, b);
+	}
+
+	public static boolean isEmpty(String string) {
+		return string == null ? true : string.trim().length() <= 0;
+	}
+
+	public static String stringInc(String str) {
+		int i = parseInt(str, -1);
+
+		if (i == -1) {
+			return "";
+		} else {
+			++i;
+			String s = "" + i;
+			return s.length() > str.length() ? "" : fillLeft("" + i, str.length(), '0');
+		}
+	}
+
+	public static int parseInt(String s, int defVal) {
+		if (s == null) {
+			return defVal;
+		} else {
+			try {
+				return Integer.parseInt(s);
+			} catch (NumberFormatException var3) {
+				return defVal;
+			}
+		}
+	}
+
+	public static boolean isFilled(String string) {
+		return !isEmpty(string);
+	}
+
+	public static String addIfNotContains(String target, String source) {
+		for (int i = 0; i < source.length(); ++i) {
+			if (target.indexOf(source.charAt(i)) < 0) {
+				target = target + source.charAt(i);
+			}
+		}
+
+		return target;
+	}
+
+	public static String fillLeft(String s, int len, char fillChar) {
+		if (s == null) {
+			s = "";
+		}
+
+		if (s.length() >= len) {
+			return s;
+		} else {
+			StringBuffer stringbuffer = new StringBuffer();
+			int i = len - s.length();
+
+			while (stringbuffer.length() < i) {
+				stringbuffer.append(fillChar);
+			}
+
+			return stringbuffer.toString() + s;
+		}
+	}
+
+	public static String fillRight(String s, int len, char fillChar) {
+		if (s == null) {
+			s = "";
+		}
+
+		if (s.length() >= len) {
+			return s;
+		} else {
+			StringBuffer stringbuffer = new StringBuffer(s);
+
+			while (stringbuffer.length() < len) {
+				stringbuffer.append(fillChar);
+			}
+
+			return stringbuffer.toString();
+		}
+	}
+
+	public static boolean equals(Object a, Object b) {
+		return a == b ? true : (a != null && a.equals(b) ? true : b != null && b.equals(a));
+	}
+
+	public static boolean startsWith(String str, String[] prefixes) {
+		if (str == null) {
+			return false;
+		} else if (prefixes == null) {
+			return false;
+		} else {
+			for (int i = 0; i < prefixes.length; ++i) {
+				String s = prefixes[i];
+
+				if (str.startsWith(s)) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+
+	public static boolean endsWith(String str, String[] suffixes) {
+		if (str == null) {
+			return false;
+		} else if (suffixes == null) {
+			return false;
+		} else {
+			for (int i = 0; i < suffixes.length; ++i) {
+				String s = suffixes[i];
+
+				if (str.endsWith(s)) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+
+	public static String removePrefix(String str, String prefix) {
+		if (str != null && prefix != null) {
+			if (str.startsWith(prefix)) {
+				str = str.substring(prefix.length());
+			}
+
+			return str;
+		} else {
+			return str;
+		}
+	}
+
+	public static String removeSuffix(String str, String suffix) {
+		if (str != null && suffix != null) {
+			if (str.endsWith(suffix)) {
+				str = str.substring(0, str.length() - suffix.length());
+			}
+
+			return str;
+		} else {
+			return str;
+		}
+	}
+
+	public static String replaceSuffix(String str, String suffix, String suffixNew) {
+		if (str != null && suffix != null) {
+			if (!str.endsWith(suffix)) {
+				return str;
+			} else {
+				if (suffixNew == null) {
+					suffixNew = "";
+				}
+
+				str = str.substring(0, str.length() - suffix.length());
+				return str + suffixNew;
+			}
+		} else {
+			return str;
+		}
+	}
+
+	public static String replacePrefix(String str, String prefix, String prefixNew) {
+		if (str != null && prefix != null) {
+			if (!str.startsWith(prefix)) {
+				return str;
+			} else {
+				if (prefixNew == null) {
+					prefixNew = "";
+				}
+
+				str = str.substring(prefix.length());
+				return prefixNew + str;
+			}
+		} else {
+			return str;
+		}
+	}
+
+	public static int findPrefix(String[] strs, String prefix) {
+		if (strs != null && prefix != null) {
+			for (int i = 0; i < strs.length; ++i) {
+				String s = strs[i];
+
+				if (s.startsWith(prefix)) {
+					return i;
+				}
+			}
+
+			return -1;
+		} else {
+			return -1;
+		}
+	}
+
+	public static int findSuffix(String[] strs, String suffix) {
+		if (strs != null && suffix != null) {
+			for (int i = 0; i < strs.length; ++i) {
+				String s = strs[i];
+
+				if (s.endsWith(suffix)) {
+					return i;
+				}
+			}
+
+			return -1;
+		} else {
+			return -1;
+		}
+	}
+
+	public static String[] remove(String[] strs, int start, int end) {
+		if (strs == null) {
+			return strs;
+		} else if (end > 0 && start < strs.length) {
+			if (start >= end) {
+				return strs;
+			} else {
+				List<String> list = new ArrayList(strs.length);
+
+				for (int i = 0; i < strs.length; ++i) {
+					String s = strs[i];
+
+					if (i < start || i >= end) {
+						list.add(s);
+					}
+				}
+
+				String[] astring = (String[]) list.toArray(new String[list.size()]);
+				return astring;
+			}
+		} else {
+			return strs;
+		}
+	}
+
+	public static String removeSuffix(String str, String[] suffixes) {
+		if (str != null && suffixes != null) {
+			int i = str.length();
+
+			for (int j = 0; j < suffixes.length; ++j) {
+				String s = suffixes[j];
+				str = removeSuffix(str, s);
+
+				if (str.length() != i) {
+					break;
+				}
+			}
+
+			return str;
+		} else {
+			return str;
+		}
+	}
+
+	public static String removePrefix(String str, String[] prefixes) {
+		if (str != null && prefixes != null) {
+			int i = str.length();
+
+			for (int j = 0; j < prefixes.length; ++j) {
+				String s = prefixes[j];
+				str = removePrefix(str, s);
+
+				if (str.length() != i) {
+					break;
+				}
+			}
+
+			return str;
+		} else {
+			return str;
+		}
+	}
+
+	public static String removePrefixSuffix(String str, String[] prefixes, String[] suffixes) {
+		str = removePrefix(str, prefixes);
+		str = removeSuffix(str, suffixes);
+		return str;
+	}
+
+	public static String removePrefixSuffix(String str, String prefix, String suffix) {
+		return removePrefixSuffix(str, new String[] { prefix }, new String[] { suffix });
+	}
+
+	public static String getSegment(String str, String start, String end) {
+		if (str != null && start != null && end != null) {
+			int i = str.indexOf(start);
+
+			if (i < 0) {
+				return null;
+			} else {
+				int j = str.indexOf(end, i);
+				return j < 0 ? null : str.substring(i, j + end.length());
+			}
+		} else {
+			return null;
+		}
+	}
+
+	public static String addSuffixCheck(String str, String suffix) {
+		return str != null && suffix != null ? (str.endsWith(suffix) ? str : str + suffix) : str;
+	}
+
+	public static String addPrefixCheck(String str, String prefix) {
+		return str != null && prefix != null ? (str.endsWith(prefix) ? str : prefix + str) : str;
+	}
+
+	public static String trim(String str, String chars) {
+		if (str != null && chars != null) {
+			str = trimLeading(str, chars);
+			str = trimTrailing(str, chars);
+			return str;
+		} else {
+			return str;
+		}
+	}
+
+	public static String trimLeading(String str, String chars) {
+		if (str != null && chars != null) {
+			int i = str.length();
+
+			for (int j = 0; j < i; ++j) {
+				char c0 = str.charAt(j);
+
+				if (chars.indexOf(c0) < 0) {
+					return str.substring(j);
+				}
+			}
+
+			return "";
+		} else {
+			return str;
+		}
+	}
+
+	public static String trimTrailing(String str, String chars) {
+		if (str != null && chars != null) {
+			int i = str.length();
+			int j;
+
+			for (j = i; j > 0; --j) {
+				char c0 = str.charAt(j - 1);
+
+				if (chars.indexOf(c0) < 0) {
+					break;
+				}
+			}
+
+			return j == i ? str : str.substring(0, j);
+		} else {
+			return str;
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/util/TextureUtils.java b/sources/main/java/net/optifine/util/TextureUtils.java
new file mode 100644
index 00000000..e54e5659
--- /dev/null
+++ b/sources/main/java/net/optifine/util/TextureUtils.java
@@ -0,0 +1,96 @@
+package net.optifine.util;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.resources.IReloadableResourceManager;
+import net.minecraft.client.resources.IResourceManager;
+import net.minecraft.client.resources.IResourceManagerReloadListener;
+import net.optifine.BetterGrass;
+import net.optifine.BetterSnow;
+import net.optifine.Config;
+import net.optifine.CustomItems;
+import net.optifine.CustomSky;
+import net.optifine.SmartLeaves;
+
+public class TextureUtils {
+
+	public static String fixResourcePath(String path, String basePath) {
+		String s = "assets/minecraft/";
+
+		if (path.startsWith(s)) {
+			path = path.substring(s.length());
+			return path;
+		} else if (path.startsWith("./")) {
+			path = path.substring(2);
+
+			if (!basePath.endsWith("/")) {
+				basePath = basePath + "/";
+			}
+
+			path = basePath + path;
+			return path;
+		} else {
+			if (path.startsWith("/~")) {
+				path = path.substring(1);
+			}
+
+			String s1 = "mcpatcher/";
+
+			if (path.startsWith("~/")) {
+				path = path.substring(2);
+				path = s1 + path;
+				return path;
+			} else if (path.startsWith("/")) {
+				path = s1 + path.substring(1);
+				return path;
+			} else {
+				return path;
+			}
+		}
+	}
+
+	public static String getBasePath(String path) {
+		int i = path.lastIndexOf(47);
+		return i < 0 ? "" : path.substring(0, i);
+	}
+
+	public static void registerResourceListener() {
+		IResourceManager iresourcemanager = Minecraft.getMinecraft().getResourceManager();
+		if (iresourcemanager instanceof IReloadableResourceManager) {
+			IReloadableResourceManager ireloadableresourcemanager = (IReloadableResourceManager) iresourcemanager;
+			IResourceManagerReloadListener iresourcemanagerreloadlistener = new IResourceManagerReloadListener() {
+				public void onResourceManagerReload(IResourceManager var1) {
+					TextureUtils.resourcesReloaded(var1);
+				}
+			};
+			ireloadableresourcemanager.registerReloadListener(iresourcemanagerreloadlistener);
+		}
+	}
+
+	public static void resourcesReloaded(IResourceManager rm) {
+		if (Minecraft.getMinecraft().getTextureMapBlocks() != null) {
+			Config.dbg("*** Reloading custom textures ***");
+			CustomSky.reset();
+			// TextureAnimations.reset();
+			// update();
+			// NaturalTextures.update();
+			BetterGrass.update();
+			BetterSnow.update();
+			// TextureAnimations.update();
+			// CustomColors.update();
+			CustomSky.update();
+			// RandomEntities.update();
+			CustomItems.updateModels();
+			// CustomEntityModels.update();
+			// Shaders.resourcesReloaded();
+			// Lang.resourcesReloaded();
+			// Config.updateTexturePackClouds();
+			SmartLeaves.updateLeavesModels();
+			// CustomPanorama.update();
+			// CustomGuis.update();
+			// LayerMooshroomMushroom.update();
+			// CustomLoadingScreens.update();
+			// CustomBlockLayers.update();
+			Minecraft.getMinecraft().getTextureManager().tick();
+		}
+	}
+}
diff --git a/sources/main/java/net/optifine/util/TileEntityUtils.java b/sources/main/java/net/optifine/util/TileEntityUtils.java
new file mode 100644
index 00000000..c21ffccc
--- /dev/null
+++ b/sources/main/java/net/optifine/util/TileEntityUtils.java
@@ -0,0 +1,22 @@
+package net.optifine.util;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.BlockPos;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.IWorldNameable;
+
+public class TileEntityUtils {
+	public static String getTileEntityName(IBlockAccess blockAccess, BlockPos blockPos) {
+		TileEntity tileentity = blockAccess.getTileEntity(blockPos);
+		return getTileEntityName(tileentity);
+	}
+
+	public static String getTileEntityName(TileEntity te) {
+		if (!(te instanceof IWorldNameable)) {
+			return null;
+		} else {
+			IWorldNameable iworldnameable = (IWorldNameable) te;
+			return !iworldnameable.hasCustomName() ? null : iworldnameable.getName();
+		}
+	}
+}
diff --git a/sources/main/java/org/apache/commons/lang3/StringEscapeUtils.java b/sources/main/java/org/apache/commons/lang3/StringEscapeUtils.java
new file mode 100644
index 00000000..45f850a1
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/StringEscapeUtils.java
@@ -0,0 +1,849 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.commons.lang3.text.translate.AggregateTranslator;
+import org.apache.commons.lang3.text.translate.CharSequenceTranslator;
+import org.apache.commons.lang3.text.translate.EntityArrays;
+import org.apache.commons.lang3.text.translate.JavaUnicodeEscaper;
+import org.apache.commons.lang3.text.translate.LookupTranslator;
+import org.apache.commons.lang3.text.translate.NumericEntityEscaper;
+import org.apache.commons.lang3.text.translate.NumericEntityUnescaper;
+import org.apache.commons.lang3.text.translate.OctalUnescaper;
+import org.apache.commons.lang3.text.translate.UnicodeUnescaper;
+import org.apache.commons.lang3.text.translate.UnicodeUnpairedSurrogateRemover;
+
+/**
+ * <p>
+ * Escapes and unescapes {@code String}s for Java, Java Script, HTML and XML.
+ * </p>
+ *
+ * <p>
+ * #ThreadSafe#
+ * </p>
+ * 
+ * @since 2.0
+ * @version $Id: StringEscapeUtils.java 1583482 2014-03-31 22:54:57Z niallp $
+ */
+public class StringEscapeUtils {
+
+	/* ESCAPE TRANSLATORS */
+
+	/**
+	 * Translator object for escaping Java.
+	 * 
+	 * While {@link #escapeJava(String)} is the expected method of use, this object
+	 * allows the Java escaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator ESCAPE_JAVA = new LookupTranslator(
+			new String[][] { { "\"", "\\\"" }, { "\\", "\\\\" }, })
+			.with(new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()))
+			.with(JavaUnicodeEscaper.outsideOf(32, 0x7f));
+
+	/**
+	 * Translator object for escaping EcmaScript/JavaScript.
+	 * 
+	 * While {@link #escapeEcmaScript(String)} is the expected method of use, this
+	 * object allows the EcmaScript escaping functionality to be used as the
+	 * foundation for a custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator ESCAPE_ECMASCRIPT = new AggregateTranslator(
+			new LookupTranslator(new String[][] { { "'", "\\'" }, { "\"", "\\\"" }, { "\\", "\\\\" }, { "/", "\\/" } }),
+			new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()), JavaUnicodeEscaper.outsideOf(32, 0x7f));
+
+	/**
+	 * Translator object for escaping Json.
+	 *
+	 * While {@link #escapeJson(String)} is the expected method of use, this object
+	 * allows the Json escaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.2
+	 */
+	public static final CharSequenceTranslator ESCAPE_JSON = new AggregateTranslator(
+			new LookupTranslator(new String[][] { { "\"", "\\\"" }, { "\\", "\\\\" }, { "/", "\\/" } }),
+			new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()), JavaUnicodeEscaper.outsideOf(32, 0x7f));
+
+	/**
+	 * Translator object for escaping XML.
+	 * 
+	 * While {@link #escapeXml(String)} is the expected method of use, this object
+	 * allows the XML escaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.0
+	 * @deprecated use {@link #ESCAPE_XML10} or {@link #ESCAPE_XML11} instead.
+	 */
+	@Deprecated
+	public static final CharSequenceTranslator ESCAPE_XML = new AggregateTranslator(
+			new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.APOS_ESCAPE()));
+
+	/**
+	 * Translator object for escaping XML 1.0.
+	 * 
+	 * While {@link #escapeXml10(String)} is the expected method of use, this object
+	 * allows the XML escaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.3
+	 */
+	public static final CharSequenceTranslator ESCAPE_XML10 = new AggregateTranslator(
+			new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.APOS_ESCAPE()),
+			new LookupTranslator(new String[][] { { "\u0000", "" }, { "\u0001", "" }, { "\u0002", "" },
+					{ "\u0003", "" }, { "\u0004", "" }, { "\u0005", "" }, { "\u0006", "" }, { "\u0007", "" },
+					{ "\u0008", "" }, { "\u000b", "" }, { "\u000c", "" }, { "\u000e", "" }, { "\u000f", "" },
+					{ "\u0010", "" }, { "\u0011", "" }, { "\u0012", "" }, { "\u0013", "" }, { "\u0014", "" },
+					{ "\u0015", "" }, { "\u0016", "" }, { "\u0017", "" }, { "\u0018", "" }, { "\u0019", "" },
+					{ "\u001a", "" }, { "\u001b", "" }, { "\u001c", "" }, { "\u001d", "" }, { "\u001e", "" },
+					{ "\u001f", "" }, { "\ufffe", "" }, { "\uffff", "" } }),
+			NumericEntityEscaper.between(0x7f, 0x84), NumericEntityEscaper.between(0x86, 0x9f),
+			new UnicodeUnpairedSurrogateRemover());
+
+	/**
+	 * Translator object for escaping XML 1.1.
+	 * 
+	 * While {@link #escapeXml11(String)} is the expected method of use, this object
+	 * allows the XML escaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.3
+	 */
+	public static final CharSequenceTranslator ESCAPE_XML11 = new AggregateTranslator(
+			new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.APOS_ESCAPE()),
+			new LookupTranslator(new String[][] { { "\u0000", "" }, { "\u000b", "&#11;" }, { "\u000c", "&#12;" },
+					{ "\ufffe", "" }, { "\uffff", "" } }),
+			NumericEntityEscaper.between(0x1, 0x8), NumericEntityEscaper.between(0xe, 0x1f),
+			NumericEntityEscaper.between(0x7f, 0x84), NumericEntityEscaper.between(0x86, 0x9f),
+			new UnicodeUnpairedSurrogateRemover());
+
+	/**
+	 * Translator object for escaping HTML version 3.0.
+	 * 
+	 * While {@link #escapeHtml3(String)} is the expected method of use, this object
+	 * allows the HTML escaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator ESCAPE_HTML3 = new AggregateTranslator(
+			new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()));
+
+	/**
+	 * Translator object for escaping HTML version 4.0.
+	 * 
+	 * While {@link #escapeHtml4(String)} is the expected method of use, this object
+	 * allows the HTML escaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator ESCAPE_HTML4 = new AggregateTranslator(
+			new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()),
+			new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE()));
+
+	/**
+	 * Translator object for escaping individual Comma Separated Values.
+	 * 
+	 * While {@link #escapeCsv(String)} is the expected method of use, this object
+	 * allows the CSV escaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator ESCAPE_CSV = new CsvEscaper();
+
+	// TODO: Create a parent class - 'SinglePassTranslator' ?
+	// It would handle the index checking + length returning,
+	// and could also have an optimization check method.
+	static class CsvEscaper extends CharSequenceTranslator {
+
+		private static final char CSV_DELIMITER = ',';
+		private static final char CSV_QUOTE = '"';
+		private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
+		private static final char[] CSV_SEARCH_CHARS = new char[] { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR,
+				CharUtils.LF };
+
+		@Override
+		public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
+
+			if (index != 0) {
+				throw new IllegalStateException("CsvEscaper should never reach the [1] index");
+			}
+
+			if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) {
+				out.write(input.toString());
+			} else {
+				out.write(CSV_QUOTE);
+				out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR));
+				out.write(CSV_QUOTE);
+			}
+			return Character.codePointCount(input, 0, input.length());
+		}
+	}
+
+	/* UNESCAPE TRANSLATORS */
+
+	/**
+	 * Translator object for unescaping escaped Java.
+	 * 
+	 * While {@link #unescapeJava(String)} is the expected method of use, this
+	 * object allows the Java unescaping functionality to be used as the foundation
+	 * for a custom translator.
+	 *
+	 * @since 3.0
+	 */
+	// TODO: throw "illegal character: \92" as an Exception if a \ on the end of the
+	// Java (as per the compiler)?
+	public static final CharSequenceTranslator UNESCAPE_JAVA = new AggregateTranslator(new OctalUnescaper(), // .between('\1',
+																												// '\377'),
+			new UnicodeUnescaper(), new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE()),
+			new LookupTranslator(new String[][] { { "\\\\", "\\" }, { "\\\"", "\"" }, { "\\'", "'" }, { "\\", "" } }));
+
+	/**
+	 * Translator object for unescaping escaped EcmaScript.
+	 * 
+	 * While {@link #unescapeEcmaScript(String)} is the expected method of use, this
+	 * object allows the EcmaScript unescaping functionality to be used as the
+	 * foundation for a custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator UNESCAPE_ECMASCRIPT = UNESCAPE_JAVA;
+
+	/**
+	 * Translator object for unescaping escaped Json.
+	 *
+	 * While {@link #unescapeJson(String)} is the expected method of use, this
+	 * object allows the Json unescaping functionality to be used as the foundation
+	 * for a custom translator.
+	 *
+	 * @since 3.2
+	 */
+	public static final CharSequenceTranslator UNESCAPE_JSON = UNESCAPE_JAVA;
+
+	/**
+	 * Translator object for unescaping escaped HTML 3.0.
+	 * 
+	 * While {@link #unescapeHtml3(String)} is the expected method of use, this
+	 * object allows the HTML unescaping functionality to be used as the foundation
+	 * for a custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator UNESCAPE_HTML3 = new AggregateTranslator(
+			new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
+			new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()), new NumericEntityUnescaper());
+
+	/**
+	 * Translator object for unescaping escaped HTML 4.0.
+	 * 
+	 * While {@link #unescapeHtml4(String)} is the expected method of use, this
+	 * object allows the HTML unescaping functionality to be used as the foundation
+	 * for a custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator UNESCAPE_HTML4 = new AggregateTranslator(
+			new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
+			new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
+			new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE()), new NumericEntityUnescaper());
+
+	/**
+	 * Translator object for unescaping escaped XML.
+	 * 
+	 * While {@link #unescapeXml(String)} is the expected method of use, this object
+	 * allows the XML unescaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator UNESCAPE_XML = new AggregateTranslator(
+			new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), new LookupTranslator(EntityArrays.APOS_UNESCAPE()),
+			new NumericEntityUnescaper());
+
+	/**
+	 * Translator object for unescaping escaped Comma Separated Value entries.
+	 * 
+	 * While {@link #unescapeCsv(String)} is the expected method of use, this object
+	 * allows the CSV unescaping functionality to be used as the foundation for a
+	 * custom translator.
+	 *
+	 * @since 3.0
+	 */
+	public static final CharSequenceTranslator UNESCAPE_CSV = new CsvUnescaper();
+
+	static class CsvUnescaper extends CharSequenceTranslator {
+
+		private static final char CSV_DELIMITER = ',';
+		private static final char CSV_QUOTE = '"';
+		private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
+		private static final char[] CSV_SEARCH_CHARS = new char[] { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR,
+				CharUtils.LF };
+
+		@Override
+		public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
+
+			if (index != 0) {
+				throw new IllegalStateException("CsvUnescaper should never reach the [1] index");
+			}
+
+			if (input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE) {
+				out.write(input.toString());
+				return Character.codePointCount(input, 0, input.length());
+			}
+
+			// strip quotes
+			final String quoteless = input.subSequence(1, input.length() - 1).toString();
+
+			if (StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS)) {
+				// deal with escaped quotes; ie) ""
+				out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR));
+			} else {
+				out.write(input.toString());
+			}
+			return Character.codePointCount(input, 0, input.length());
+		}
+	}
+
+	/* Helper functions */
+
+	/**
+	 * <p>
+	 * {@code StringEscapeUtils} instances should NOT be constructed in standard
+	 * programming.
+	 * </p>
+	 *
+	 * <p>
+	 * Instead, the class should be used as:
+	 * </p>
+	 * 
+	 * <pre>
+	 * StringEscapeUtils.escapeJava("foo");
+	 * </pre>
+	 *
+	 * <p>
+	 * This constructor is public to permit tools that require a JavaBean instance
+	 * to operate.
+	 * </p>
+	 */
+	public StringEscapeUtils() {
+		super();
+	}
+
+	// Java and JavaScript
+	// --------------------------------------------------------------------------
+	/**
+	 * <p>
+	 * Escapes the characters in a {@code String} using Java String rules.
+	 * </p>
+	 *
+	 * <p>
+	 * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.)
+	 * </p>
+	 *
+	 * <p>
+	 * So a tab becomes the characters {@code '\\'} and {@code 't'}.
+	 * </p>
+	 *
+	 * <p>
+	 * The only difference between Java strings and JavaScript strings is that in
+	 * JavaScript, a single quote and forward-slash (/) are escaped.
+	 * </p>
+	 *
+	 * <p>
+	 * Example:
+	 * </p>
+	 * 
+	 * <pre>
+	 * input string: He didn't say, "Stop!"
+	 * output string: He didn't say, \"Stop!\"
+	 * </pre>
+	 *
+	 * @param input String to escape values in, may be null
+	 * @return String with escaped values, {@code null} if null string input
+	 */
+	public static final String escapeJava(final String input) {
+		return ESCAPE_JAVA.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Escapes the characters in a {@code String} using EcmaScript String rules.
+	 * </p>
+	 * <p>
+	 * Escapes any values it finds into their EcmaScript String form. Deals
+	 * correctly with quotes and control-chars (tab, backslash, cr, ff, etc.)
+	 * </p>
+	 *
+	 * <p>
+	 * So a tab becomes the characters {@code '\\'} and {@code 't'}.
+	 * </p>
+	 *
+	 * <p>
+	 * The only difference between Java strings and EcmaScript strings is that in
+	 * EcmaScript, a single quote and forward-slash (/) are escaped.
+	 * </p>
+	 *
+	 * <p>
+	 * Note that EcmaScript is best known by the JavaScript and ActionScript
+	 * dialects.
+	 * </p>
+	 *
+	 * <p>
+	 * Example:
+	 * </p>
+	 * 
+	 * <pre>
+	 * input string: He didn't say, "Stop!"
+	 * output string: He didn\'t say, \"Stop!\"
+	 * </pre>
+	 *
+	 * @param input String to escape values in, may be null
+	 * @return String with escaped values, {@code null} if null string input
+	 *
+	 * @since 3.0
+	 */
+	public static final String escapeEcmaScript(final String input) {
+		return ESCAPE_ECMASCRIPT.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Escapes the characters in a {@code String} using Json String rules.
+	 * </p>
+	 * <p>
+	 * Escapes any values it finds into their Json String form. Deals correctly with
+	 * quotes and control-chars (tab, backslash, cr, ff, etc.)
+	 * </p>
+	 *
+	 * <p>
+	 * So a tab becomes the characters {@code '\\'} and {@code 't'}.
+	 * </p>
+	 *
+	 * <p>
+	 * The only difference between Java strings and Json strings is that in Json,
+	 * forward-slash (/) is escaped.
+	 * </p>
+	 *
+	 * <p>
+	 * See http://www.ietf.org/rfc/rfc4627.txt for further details.
+	 * </p>
+	 *
+	 * <p>
+	 * Example:
+	 * </p>
+	 * 
+	 * <pre>
+	 * input string: He didn't say, "Stop!"
+	 * output string: He didn't say, \"Stop!\"
+	 * </pre>
+	 *
+	 * @param input String to escape values in, may be null
+	 * @return String with escaped values, {@code null} if null string input
+	 *
+	 * @since 3.2
+	 */
+	public static final String escapeJson(final String input) {
+		return ESCAPE_JSON.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Unescapes any Java literals found in the {@code String}. For example, it will
+	 * turn a sequence of {@code '\'} and {@code 'n'} into a newline character,
+	 * unless the {@code '\'} is preceded by another {@code '\'}.
+	 * </p>
+	 * 
+	 * @param input the {@code String} to unescape, may be null
+	 * @return a new unescaped {@code String}, {@code null} if null string input
+	 */
+	public static final String unescapeJava(final String input) {
+		return UNESCAPE_JAVA.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Unescapes any EcmaScript literals found in the {@code String}.
+	 * </p>
+	 *
+	 * <p>
+	 * For example, it will turn a sequence of {@code '\'} and {@code 'n'} into a
+	 * newline character, unless the {@code '\'} is preceded by another {@code '\'}.
+	 * </p>
+	 *
+	 * @see #unescapeJava(String)
+	 * @param input the {@code String} to unescape, may be null
+	 * @return A new unescaped {@code String}, {@code null} if null string input
+	 *
+	 * @since 3.0
+	 */
+	public static final String unescapeEcmaScript(final String input) {
+		return UNESCAPE_ECMASCRIPT.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Unescapes any Json literals found in the {@code String}.
+	 * </p>
+	 *
+	 * <p>
+	 * For example, it will turn a sequence of {@code '\'} and {@code 'n'} into a
+	 * newline character, unless the {@code '\'} is preceded by another {@code '\'}.
+	 * </p>
+	 *
+	 * @see #unescapeJava(String)
+	 * @param input the {@code String} to unescape, may be null
+	 * @return A new unescaped {@code String}, {@code null} if null string input
+	 *
+	 * @since 3.2
+	 */
+	public static final String unescapeJson(final String input) {
+		return UNESCAPE_JSON.translate(input);
+	}
+
+	// HTML and XML
+	// --------------------------------------------------------------------------
+	/**
+	 * <p>
+	 * Escapes the characters in a {@code String} using HTML entities.
+	 * </p>
+	 *
+	 * <p>
+	 * For example:
+	 * </p>
+	 * <p>
+	 * <code>"bread" &amp; "butter"</code>
+	 * </p>
+	 * becomes:
+	 * <p>
+	 * <code>&amp;quot;bread&amp;quot; &amp;amp; &amp;quot;butter&amp;quot;</code>.
+	 * </p>
+	 *
+	 * <p>
+	 * Supports all known HTML 4.0 entities, including funky accents. Note that the
+	 * commonly used apostrophe escape character (&amp;apos;) is not a legal entity
+	 * and so is not supported).
+	 * </p>
+	 *
+	 * @param input the {@code String} to escape, may be null
+	 * @return a new escaped {@code String}, {@code null} if null string input
+	 * 
+	 * @see <a href=
+	 *      "http://hotwired.lycos.com/webmonkey/reference/special_characters/">ISO
+	 *      Entities</a>
+	 * @see <a href="http://www.w3.org/TR/REC-html32#latin1">HTML 3.2 Character
+	 *      Entities for ISO Latin-1</a>
+	 * @see <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">HTML 4.0
+	 *      Character entity references</a>
+	 * @see <a href="http://www.w3.org/TR/html401/charset.html#h-5.3">HTML 4.01
+	 *      Character References</a>
+	 * @see <a href="http://www.w3.org/TR/html401/charset.html#code-position">HTML
+	 *      4.01 Code positions</a>
+	 * 
+	 * @since 3.0
+	 */
+	public static final String escapeHtml4(final String input) {
+		return ESCAPE_HTML4.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Escapes the characters in a {@code String} using HTML entities.
+	 * </p>
+	 * <p>
+	 * Supports only the HTML 3.0 entities.
+	 * </p>
+	 *
+	 * @param input the {@code String} to escape, may be null
+	 * @return a new escaped {@code String}, {@code null} if null string input
+	 * 
+	 * @since 3.0
+	 */
+	public static final String escapeHtml3(final String input) {
+		return ESCAPE_HTML3.translate(input);
+	}
+
+	// -----------------------------------------------------------------------
+	/**
+	 * <p>
+	 * Unescapes a string containing entity escapes to a string containing the
+	 * actual Unicode characters corresponding to the escapes. Supports HTML 4.0
+	 * entities.
+	 * </p>
+	 *
+	 * <p>
+	 * For example, the string "&amp;lt;Fran&amp;ccedil;ais&amp;gt;" will become
+	 * "&lt;Fran&ccedil;ais&gt;"
+	 * </p>
+	 *
+	 * <p>
+	 * If an entity is unrecognized, it is left alone, and inserted verbatim into
+	 * the result string. e.g. "&amp;gt;&amp;zzzz;x" will become "&gt;&amp;zzzz;x".
+	 * </p>
+	 *
+	 * @param input the {@code String} to unescape, may be null
+	 * @return a new unescaped {@code String}, {@code null} if null string input
+	 * 
+	 * @since 3.0
+	 */
+	public static final String unescapeHtml4(final String input) {
+		return UNESCAPE_HTML4.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Unescapes a string containing entity escapes to a string containing the
+	 * actual Unicode characters corresponding to the escapes. Supports only HTML
+	 * 3.0 entities.
+	 * </p>
+	 *
+	 * @param input the {@code String} to unescape, may be null
+	 * @return a new unescaped {@code String}, {@code null} if null string input
+	 * 
+	 * @since 3.0
+	 */
+	public static final String unescapeHtml3(final String input) {
+		return UNESCAPE_HTML3.translate(input);
+	}
+
+	// -----------------------------------------------------------------------
+	/**
+	 * <p>
+	 * Escapes the characters in a {@code String} using XML entities.
+	 * </p>
+	 *
+	 * <p>
+	 * For example: <tt>"bread" &amp; "butter"</tt> =&gt;
+	 * <tt>&amp;quot;bread&amp;quot; &amp;amp; &amp;quot;butter&amp;quot;</tt>.
+	 * </p>
+	 *
+	 * <p>
+	 * Supports only the five basic XML entities (gt, lt, quot, amp, apos). Does not
+	 * support DTDs or external entities.
+	 * </p>
+	 *
+	 * <p>
+	 * Note that Unicode characters greater than 0x7f are as of 3.0, no longer
+	 * escaped. If you still wish this functionality, you can achieve it via the
+	 * following:
+	 * {@code StringEscapeUtils.ESCAPE_XML.with( NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE) );}
+	 * </p>
+	 *
+	 * @param input the {@code String} to escape, may be null
+	 * @return a new escaped {@code String}, {@code null} if null string input
+	 * @see #unescapeXml(java.lang.String)
+	 * @deprecated use {@link #escapeXml10(java.lang.String)} or
+	 *             {@link #escapeXml11(java.lang.String)} instead.
+	 */
+	@Deprecated
+	public static final String escapeXml(final String input) {
+		return ESCAPE_XML.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Escapes the characters in a {@code String} using XML entities.
+	 * </p>
+	 *
+	 * <p>
+	 * For example: <tt>"bread" &amp; "butter"</tt> =&gt;
+	 * <tt>&amp;quot;bread&amp;quot; &amp;amp; &amp;quot;butter&amp;quot;</tt>.
+	 * </p>
+	 *
+	 * <p>
+	 * Note that XML 1.0 is a text-only format: it cannot represent control
+	 * characters or unpaired Unicode surrogate codepoints, even after escaping.
+	 * {@code escapeXml10} will remove characters that do not fit in the following
+	 * ranges:
+	 * </p>
+	 * 
+	 * <p>
+	 * {@code #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}
+	 * </p>
+	 * 
+	 * <p>
+	 * Though not strictly necessary, {@code escapeXml10} will escape characters in
+	 * the following ranges:
+	 * </p>
+	 * 
+	 * <p>
+	 * {@code [#x7F-#x84] | [#x86-#x9F]}
+	 * </p>
+	 * 
+	 * <p>
+	 * The returned string can be inserted into a valid XML 1.0 or XML 1.1 document.
+	 * If you want to allow more non-text characters in an XML 1.1 document, use
+	 * {@link #escapeXml11(String)}.
+	 * </p>
+	 *
+	 * @param input the {@code String} to escape, may be null
+	 * @return a new escaped {@code String}, {@code null} if null string input
+	 * @see #unescapeXml(java.lang.String)
+	 * @since 3.3
+	 */
+	public static String escapeXml10(final String input) {
+		return ESCAPE_XML10.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Escapes the characters in a {@code String} using XML entities.
+	 * </p>
+	 *
+	 * <p>
+	 * For example: <tt>"bread" &amp; "butter"</tt> =&gt;
+	 * <tt>&amp;quot;bread&amp;quot; &amp;amp; &amp;quot;butter&amp;quot;</tt>.
+	 * </p>
+	 *
+	 * <p>
+	 * XML 1.1 can represent certain control characters, but it cannot represent the
+	 * null byte or unpaired Unicode surrogate codepoints, even after escaping.
+	 * {@code escapeXml11} will remove characters that do not fit in the following
+	 * ranges:
+	 * </p>
+	 * 
+	 * <p>
+	 * {@code [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}
+	 * </p>
+	 * 
+	 * <p>
+	 * {@code escapeXml11} will escape characters in the following ranges:
+	 * </p>
+	 * 
+	 * <p>
+	 * {@code [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F]}
+	 * </p>
+	 * 
+	 * <p>
+	 * The returned string can be inserted into a valid XML 1.1 document. Do not use
+	 * it for XML 1.0 documents.
+	 * </p>
+	 *
+	 * @param input the {@code String} to escape, may be null
+	 * @return a new escaped {@code String}, {@code null} if null string input
+	 * @see #unescapeXml(java.lang.String)
+	 * @since 3.3
+	 */
+	public static String escapeXml11(final String input) {
+		return ESCAPE_XML11.translate(input);
+	}
+
+	// -----------------------------------------------------------------------
+	/**
+	 * <p>
+	 * Unescapes a string containing XML entity escapes to a string containing the
+	 * actual Unicode characters corresponding to the escapes.
+	 * </p>
+	 *
+	 * <p>
+	 * Supports only the five basic XML entities (gt, lt, quot, amp, apos). Does not
+	 * support DTDs or external entities.
+	 * </p>
+	 *
+	 * <p>
+	 * Note that numerical \\u Unicode codes are unescaped to their respective
+	 * Unicode characters. This may change in future releases.
+	 * </p>
+	 *
+	 * @param input the {@code String} to unescape, may be null
+	 * @return a new unescaped {@code String}, {@code null} if null string input
+	 * @see #escapeXml(String)
+	 * @see #escapeXml10(String)
+	 * @see #escapeXml11(String)
+	 */
+	public static final String unescapeXml(final String input) {
+		return UNESCAPE_XML.translate(input);
+	}
+
+	// -----------------------------------------------------------------------
+
+	/**
+	 * <p>
+	 * Returns a {@code String} value for a CSV column enclosed in double quotes, if
+	 * required.
+	 * </p>
+	 *
+	 * <p>
+	 * If the value contains a comma, newline or double quote, then the String value
+	 * is returned enclosed in double quotes.
+	 * </p>
+	 *
+	 * <p>
+	 * Any double quote characters in the value are escaped with another double
+	 * quote.
+	 * </p>
+	 *
+	 * <p>
+	 * If the value does not contain a comma, newline or double quote, then the
+	 * String value is returned unchanged.
+	 * </p>
+	 *
+	 * see
+	 * <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a>
+	 * and <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
+	 *
+	 * @param input the input CSV column String, may be null
+	 * @return the input String, enclosed in double quotes if the value contains a
+	 *         comma, newline or double quote, {@code null} if null string input
+	 * @since 2.4
+	 */
+	public static final String escapeCsv(final String input) {
+		return ESCAPE_CSV.translate(input);
+	}
+
+	/**
+	 * <p>
+	 * Returns a {@code String} value for an unescaped CSV column.
+	 * </p>
+	 *
+	 * <p>
+	 * If the value is enclosed in double quotes, and contains a comma, newline or
+	 * double quote, then quotes are removed.
+	 * </p>
+	 *
+	 * <p>
+	 * Any double quote escaped characters (a pair of double quotes) are unescaped
+	 * to just one double quote.
+	 * </p>
+	 *
+	 * <p>
+	 * If the value is not enclosed in double quotes, or is and does not contain a
+	 * comma, newline or double quote, then the String value is returned unchanged.
+	 * </p>
+	 *
+	 * see
+	 * <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a>
+	 * and <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
+	 *
+	 * @param input the input CSV column String, may be null
+	 * @return the input String, with enclosing double quotes removed and embedded
+	 *         double quotes unescaped, {@code null} if null string input
+	 * @since 2.4
+	 */
+	public static final String unescapeCsv(final String input) {
+		return UNESCAPE_CSV.translate(input);
+	}
+
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/AggregateTranslator.java b/sources/main/java/org/apache/commons/lang3/text/translate/AggregateTranslator.java
new file mode 100644
index 00000000..829a01f2
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/AggregateTranslator.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Executes a sequence of translators one after the other. Execution ends
+ * whenever the first translator consumes codepoints from the input.
+ * 
+ * @since 3.0
+ * @version $Id: AggregateTranslator.java 1436770 2013-01-22 07:09:45Z ggregory
+ *          $
+ */
+public class AggregateTranslator extends CharSequenceTranslator {
+
+	private final CharSequenceTranslator[] translators;
+
+	/**
+	 * Specify the translators to be used at creation time.
+	 *
+	 * @param translators CharSequenceTranslator array to aggregate
+	 */
+	public AggregateTranslator(final CharSequenceTranslator... translators) {
+		this.translators = new CharSequenceTranslator[translators.length];
+		System.arraycopy(translators, 0, this.translators, 0, translators.length);
+	}
+
+	/**
+	 * The first translator to consume codepoints from the input is the 'winner'.
+	 * Execution stops with the number of consumed codepoints being returned.
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
+		for (final CharSequenceTranslator translator : translators) {
+			final int consumed = translator.translate(input, index, out);
+			if (consumed != 0) {
+				return consumed;
+			}
+		}
+		return 0;
+	}
+
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java b/sources/main/java/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java
new file mode 100644
index 00000000..cec33dc6
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+/**
+ * An API for translating text. Its core use is to escape and unescape text.
+ * Because escaping and unescaping is completely contextual, the API does not
+ * present two separate signatures.
+ * 
+ * @since 3.0
+ * @version $Id: CharSequenceTranslator.java 1568612 2014-02-15 10:35:35Z
+ *          britter $
+ */
+public abstract class CharSequenceTranslator {
+
+	/**
+	 * Translate a set of codepoints, represented by an int index into a
+	 * CharSequence, into another set of codepoints. The number of codepoints
+	 * consumed must be returned, and the only IOExceptions thrown must be from
+	 * interacting with the Writer so that the top level API may reliably ignore
+	 * StringWriter IOExceptions.
+	 *
+	 * @param input CharSequence that is being translated
+	 * @param index int representing the current point of translation
+	 * @param out   Writer to translate the text to
+	 * @return int count of codepoints consumed
+	 * @throws IOException if and only if the Writer produces an IOException
+	 */
+	public abstract int translate(CharSequence input, int index, Writer out) throws IOException;
+
+	/**
+	 * Helper for non-Writer usage.
+	 * 
+	 * @param input CharSequence to be translated
+	 * @return String output of translation
+	 */
+	public final String translate(final CharSequence input) {
+		if (input == null) {
+			return null;
+		}
+		try {
+			final StringWriter writer = new StringWriter(input.length() * 2);
+			translate(input, writer);
+			return writer.toString();
+		} catch (final IOException ioe) {
+			// this should never ever happen while writing to a StringWriter
+			throw new RuntimeException(ioe);
+		}
+	}
+
+	/**
+	 * Translate an input onto a Writer. This is intentionally final as its
+	 * algorithm is tightly coupled with the abstract method of this class.
+	 *
+	 * @param input CharSequence that is being translated
+	 * @param out   Writer to translate the text to
+	 * @throws IOException if and only if the Writer produces an IOException
+	 */
+	public final void translate(final CharSequence input, final Writer out) throws IOException {
+		if (out == null) {
+			throw new IllegalArgumentException("The Writer must not be null");
+		}
+		if (input == null) {
+			return;
+		}
+		int pos = 0;
+		final int len = input.length();
+		while (pos < len) {
+			final int consumed = translate(input, pos, out);
+			if (consumed == 0) {
+				final char[] c = Character.toChars(Character.codePointAt(input, pos));
+				out.write(c);
+				pos += c.length;
+				continue;
+			}
+			// contract with translators is that they have to understand codepoints
+			// and they just took care of a surrogate pair
+			for (int pt = 0; pt < consumed; pt++) {
+				pos += Character.charCount(Character.codePointAt(input, pos));
+			}
+		}
+	}
+
+	/**
+	 * Helper method to create a merger of this translator with another set of
+	 * translators. Useful in customizing the standard functionality.
+	 *
+	 * @param translators CharSequenceTranslator array of translators to merge with
+	 *                    this one
+	 * @return CharSequenceTranslator merging this translator with the others
+	 */
+	public final CharSequenceTranslator with(final CharSequenceTranslator... translators) {
+		final CharSequenceTranslator[] newArray = new CharSequenceTranslator[translators.length + 1];
+		newArray[0] = this;
+		System.arraycopy(translators, 0, newArray, 1, translators.length);
+		return new AggregateTranslator(newArray);
+	}
+
+	/**
+	 * <p>
+	 * Returns an upper case hexadecimal <code>String</code> for the given
+	 * character.
+	 * </p>
+	 *
+	 * @param codepoint The codepoint to convert.
+	 * @return An upper case hexadecimal <code>String</code>
+	 */
+	public static String hex(final int codepoint) {
+		return Integer.toHexString(codepoint).toUpperCase(Locale.ENGLISH);
+	}
+
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/CodePointTranslator.java b/sources/main/java/org/apache/commons/lang3/text/translate/CodePointTranslator.java
new file mode 100644
index 00000000..a9567293
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/CodePointTranslator.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Helper subclass to CharSequenceTranslator to allow for translations that will
+ * replace up to one character at a time.
+ * 
+ * @since 3.0
+ * @version $Id: CodePointTranslator.java 1553931 2013-12-28 21:24:44Z ggregory
+ *          $
+ */
+public abstract class CodePointTranslator extends CharSequenceTranslator {
+
+	/**
+	 * Implementation of translate that maps onto the abstract translate(int,
+	 * Writer) method. {@inheritDoc}
+	 */
+	@Override
+	public final int translate(final CharSequence input, final int index, final Writer out) throws IOException {
+		final int codepoint = Character.codePointAt(input, index);
+		final boolean consumed = translate(codepoint, out);
+		return consumed ? 1 : 0;
+	}
+
+	/**
+	 * Translate the specified codepoint into another.
+	 * 
+	 * @param codepoint int character input to translate
+	 * @param out       Writer to optionally push the translated output to
+	 * @return boolean as to whether translation occurred or not
+	 * @throws IOException if and only if the Writer produces an IOException
+	 */
+	public abstract boolean translate(int codepoint, Writer out) throws IOException;
+
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/EntityArrays.java b/sources/main/java/org/apache/commons/lang3/text/translate/EntityArrays.java
new file mode 100644
index 00000000..20626cea
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/EntityArrays.java
@@ -0,0 +1,461 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+/**
+ * Class holding various entity data for HTML and XML - generally for use with
+ * the LookupTranslator. All arrays are of length [*][2].
+ *
+ * @since 3.0
+ * @version $Id: EntityArrays.java 1436770 2013-01-22 07:09:45Z ggregory $
+ */
+public class EntityArrays {
+
+	/**
+	 * Mapping to escape <a href=
+	 * "https://secure.wikimedia.org/wikipedia/en/wiki/ISO/IEC_8859-1">ISO-8859-1</a>
+	 * characters to their named HTML 3.x equivalents.
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] ISO8859_1_ESCAPE() {
+		return ISO8859_1_ESCAPE.clone();
+	}
+
+	private static final String[][] ISO8859_1_ESCAPE = { { "\u00A0", "&nbsp;" }, // non-breaking space
+			{ "\u00A1", "&iexcl;" }, // inverted exclamation mark
+			{ "\u00A2", "&cent;" }, // cent sign
+			{ "\u00A3", "&pound;" }, // pound sign
+			{ "\u00A4", "&curren;" }, // currency sign
+			{ "\u00A5", "&yen;" }, // yen sign = yuan sign
+			{ "\u00A6", "&brvbar;" }, // broken bar = broken vertical bar
+			{ "\u00A7", "&sect;" }, // section sign
+			{ "\u00A8", "&uml;" }, // diaeresis = spacing diaeresis
+			{ "\u00A9", "&copy;" }, // � - copyright sign
+			{ "\u00AA", "&ordf;" }, // feminine ordinal indicator
+			{ "\u00AB", "&laquo;" }, // left-pointing double angle quotation mark = left pointing guillemet
+			{ "\u00AC", "&not;" }, // not sign
+			{ "\u00AD", "&shy;" }, // soft hyphen = discretionary hyphen
+			{ "\u00AE", "&reg;" }, // � - registered trademark sign
+			{ "\u00AF", "&macr;" }, // macron = spacing macron = overline = APL overbar
+			{ "\u00B0", "&deg;" }, // degree sign
+			{ "\u00B1", "&plusmn;" }, // plus-minus sign = plus-or-minus sign
+			{ "\u00B2", "&sup2;" }, // superscript two = superscript digit two = squared
+			{ "\u00B3", "&sup3;" }, // superscript three = superscript digit three = cubed
+			{ "\u00B4", "&acute;" }, // acute accent = spacing acute
+			{ "\u00B5", "&micro;" }, // micro sign
+			{ "\u00B6", "&para;" }, // pilcrow sign = paragraph sign
+			{ "\u00B7", "&middot;" }, // middle dot = Georgian comma = Greek middle dot
+			{ "\u00B8", "&cedil;" }, // cedilla = spacing cedilla
+			{ "\u00B9", "&sup1;" }, // superscript one = superscript digit one
+			{ "\u00BA", "&ordm;" }, // masculine ordinal indicator
+			{ "\u00BB", "&raquo;" }, // right-pointing double angle quotation mark = right pointing guillemet
+			{ "\u00BC", "&frac14;" }, // vulgar fraction one quarter = fraction one quarter
+			{ "\u00BD", "&frac12;" }, // vulgar fraction one half = fraction one half
+			{ "\u00BE", "&frac34;" }, // vulgar fraction three quarters = fraction three quarters
+			{ "\u00BF", "&iquest;" }, // inverted question mark = turned question mark
+			{ "\u00C0", "&Agrave;" }, // � - uppercase A, grave accent
+			{ "\u00C1", "&Aacute;" }, // � - uppercase A, acute accent
+			{ "\u00C2", "&Acirc;" }, // � - uppercase A, circumflex accent
+			{ "\u00C3", "&Atilde;" }, // � - uppercase A, tilde
+			{ "\u00C4", "&Auml;" }, // � - uppercase A, umlaut
+			{ "\u00C5", "&Aring;" }, // � - uppercase A, ring
+			{ "\u00C6", "&AElig;" }, // � - uppercase AE
+			{ "\u00C7", "&Ccedil;" }, // � - uppercase C, cedilla
+			{ "\u00C8", "&Egrave;" }, // � - uppercase E, grave accent
+			{ "\u00C9", "&Eacute;" }, // � - uppercase E, acute accent
+			{ "\u00CA", "&Ecirc;" }, // � - uppercase E, circumflex accent
+			{ "\u00CB", "&Euml;" }, // � - uppercase E, umlaut
+			{ "\u00CC", "&Igrave;" }, // � - uppercase I, grave accent
+			{ "\u00CD", "&Iacute;" }, // � - uppercase I, acute accent
+			{ "\u00CE", "&Icirc;" }, // � - uppercase I, circumflex accent
+			{ "\u00CF", "&Iuml;" }, // � - uppercase I, umlaut
+			{ "\u00D0", "&ETH;" }, // � - uppercase Eth, Icelandic
+			{ "\u00D1", "&Ntilde;" }, // � - uppercase N, tilde
+			{ "\u00D2", "&Ograve;" }, // � - uppercase O, grave accent
+			{ "\u00D3", "&Oacute;" }, // � - uppercase O, acute accent
+			{ "\u00D4", "&Ocirc;" }, // � - uppercase O, circumflex accent
+			{ "\u00D5", "&Otilde;" }, // � - uppercase O, tilde
+			{ "\u00D6", "&Ouml;" }, // � - uppercase O, umlaut
+			{ "\u00D7", "&times;" }, // multiplication sign
+			{ "\u00D8", "&Oslash;" }, // � - uppercase O, slash
+			{ "\u00D9", "&Ugrave;" }, // � - uppercase U, grave accent
+			{ "\u00DA", "&Uacute;" }, // � - uppercase U, acute accent
+			{ "\u00DB", "&Ucirc;" }, // � - uppercase U, circumflex accent
+			{ "\u00DC", "&Uuml;" }, // � - uppercase U, umlaut
+			{ "\u00DD", "&Yacute;" }, // � - uppercase Y, acute accent
+			{ "\u00DE", "&THORN;" }, // � - uppercase THORN, Icelandic
+			{ "\u00DF", "&szlig;" }, // � - lowercase sharps, German
+			{ "\u00E0", "&agrave;" }, // � - lowercase a, grave accent
+			{ "\u00E1", "&aacute;" }, // � - lowercase a, acute accent
+			{ "\u00E2", "&acirc;" }, // � - lowercase a, circumflex accent
+			{ "\u00E3", "&atilde;" }, // � - lowercase a, tilde
+			{ "\u00E4", "&auml;" }, // � - lowercase a, umlaut
+			{ "\u00E5", "&aring;" }, // � - lowercase a, ring
+			{ "\u00E6", "&aelig;" }, // � - lowercase ae
+			{ "\u00E7", "&ccedil;" }, // � - lowercase c, cedilla
+			{ "\u00E8", "&egrave;" }, // � - lowercase e, grave accent
+			{ "\u00E9", "&eacute;" }, // � - lowercase e, acute accent
+			{ "\u00EA", "&ecirc;" }, // � - lowercase e, circumflex accent
+			{ "\u00EB", "&euml;" }, // � - lowercase e, umlaut
+			{ "\u00EC", "&igrave;" }, // � - lowercase i, grave accent
+			{ "\u00ED", "&iacute;" }, // � - lowercase i, acute accent
+			{ "\u00EE", "&icirc;" }, // � - lowercase i, circumflex accent
+			{ "\u00EF", "&iuml;" }, // � - lowercase i, umlaut
+			{ "\u00F0", "&eth;" }, // � - lowercase eth, Icelandic
+			{ "\u00F1", "&ntilde;" }, // � - lowercase n, tilde
+			{ "\u00F2", "&ograve;" }, // � - lowercase o, grave accent
+			{ "\u00F3", "&oacute;" }, // � - lowercase o, acute accent
+			{ "\u00F4", "&ocirc;" }, // � - lowercase o, circumflex accent
+			{ "\u00F5", "&otilde;" }, // � - lowercase o, tilde
+			{ "\u00F6", "&ouml;" }, // � - lowercase o, umlaut
+			{ "\u00F7", "&divide;" }, // division sign
+			{ "\u00F8", "&oslash;" }, // � - lowercase o, slash
+			{ "\u00F9", "&ugrave;" }, // � - lowercase u, grave accent
+			{ "\u00FA", "&uacute;" }, // � - lowercase u, acute accent
+			{ "\u00FB", "&ucirc;" }, // � - lowercase u, circumflex accent
+			{ "\u00FC", "&uuml;" }, // � - lowercase u, umlaut
+			{ "\u00FD", "&yacute;" }, // � - lowercase y, acute accent
+			{ "\u00FE", "&thorn;" }, // � - lowercase thorn, Icelandic
+			{ "\u00FF", "&yuml;" }, // � - lowercase y, umlaut
+	};
+
+	/**
+	 * Reverse of {@link #ISO8859_1_ESCAPE()} for unescaping purposes.
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] ISO8859_1_UNESCAPE() {
+		return ISO8859_1_UNESCAPE.clone();
+	}
+
+	private static final String[][] ISO8859_1_UNESCAPE = invert(ISO8859_1_ESCAPE);
+
+	/**
+	 * Mapping to escape additional
+	 * <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">character entity
+	 * references</a>. Note that this must be used with {@link #ISO8859_1_ESCAPE()}
+	 * to get the full list of HTML 4.0 character entities.
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] HTML40_EXTENDED_ESCAPE() {
+		return HTML40_EXTENDED_ESCAPE.clone();
+	}
+
+	private static final String[][] HTML40_EXTENDED_ESCAPE = {
+			// <!-- Latin Extended-B -->
+			{ "\u0192", "&fnof;" }, // latin small f with hook = function= florin, U+0192 ISOtech -->
+			// <!-- Greek -->
+			{ "\u0391", "&Alpha;" }, // greek capital letter alpha, U+0391 -->
+			{ "\u0392", "&Beta;" }, // greek capital letter beta, U+0392 -->
+			{ "\u0393", "&Gamma;" }, // greek capital letter gamma,U+0393 ISOgrk3 -->
+			{ "\u0394", "&Delta;" }, // greek capital letter delta,U+0394 ISOgrk3 -->
+			{ "\u0395", "&Epsilon;" }, // greek capital letter epsilon, U+0395 -->
+			{ "\u0396", "&Zeta;" }, // greek capital letter zeta, U+0396 -->
+			{ "\u0397", "&Eta;" }, // greek capital letter eta, U+0397 -->
+			{ "\u0398", "&Theta;" }, // greek capital letter theta,U+0398 ISOgrk3 -->
+			{ "\u0399", "&Iota;" }, // greek capital letter iota, U+0399 -->
+			{ "\u039A", "&Kappa;" }, // greek capital letter kappa, U+039A -->
+			{ "\u039B", "&Lambda;" }, // greek capital letter lambda,U+039B ISOgrk3 -->
+			{ "\u039C", "&Mu;" }, // greek capital letter mu, U+039C -->
+			{ "\u039D", "&Nu;" }, // greek capital letter nu, U+039D -->
+			{ "\u039E", "&Xi;" }, // greek capital letter xi, U+039E ISOgrk3 -->
+			{ "\u039F", "&Omicron;" }, // greek capital letter omicron, U+039F -->
+			{ "\u03A0", "&Pi;" }, // greek capital letter pi, U+03A0 ISOgrk3 -->
+			{ "\u03A1", "&Rho;" }, // greek capital letter rho, U+03A1 -->
+			// <!-- there is no Sigmaf, and no U+03A2 character either -->
+			{ "\u03A3", "&Sigma;" }, // greek capital letter sigma,U+03A3 ISOgrk3 -->
+			{ "\u03A4", "&Tau;" }, // greek capital letter tau, U+03A4 -->
+			{ "\u03A5", "&Upsilon;" }, // greek capital letter upsilon,U+03A5 ISOgrk3 -->
+			{ "\u03A6", "&Phi;" }, // greek capital letter phi,U+03A6 ISOgrk3 -->
+			{ "\u03A7", "&Chi;" }, // greek capital letter chi, U+03A7 -->
+			{ "\u03A8", "&Psi;" }, // greek capital letter psi,U+03A8 ISOgrk3 -->
+			{ "\u03A9", "&Omega;" }, // greek capital letter omega,U+03A9 ISOgrk3 -->
+			{ "\u03B1", "&alpha;" }, // greek small letter alpha,U+03B1 ISOgrk3 -->
+			{ "\u03B2", "&beta;" }, // greek small letter beta, U+03B2 ISOgrk3 -->
+			{ "\u03B3", "&gamma;" }, // greek small letter gamma,U+03B3 ISOgrk3 -->
+			{ "\u03B4", "&delta;" }, // greek small letter delta,U+03B4 ISOgrk3 -->
+			{ "\u03B5", "&epsilon;" }, // greek small letter epsilon,U+03B5 ISOgrk3 -->
+			{ "\u03B6", "&zeta;" }, // greek small letter zeta, U+03B6 ISOgrk3 -->
+			{ "\u03B7", "&eta;" }, // greek small letter eta, U+03B7 ISOgrk3 -->
+			{ "\u03B8", "&theta;" }, // greek small letter theta,U+03B8 ISOgrk3 -->
+			{ "\u03B9", "&iota;" }, // greek small letter iota, U+03B9 ISOgrk3 -->
+			{ "\u03BA", "&kappa;" }, // greek small letter kappa,U+03BA ISOgrk3 -->
+			{ "\u03BB", "&lambda;" }, // greek small letter lambda,U+03BB ISOgrk3 -->
+			{ "\u03BC", "&mu;" }, // greek small letter mu, U+03BC ISOgrk3 -->
+			{ "\u03BD", "&nu;" }, // greek small letter nu, U+03BD ISOgrk3 -->
+			{ "\u03BE", "&xi;" }, // greek small letter xi, U+03BE ISOgrk3 -->
+			{ "\u03BF", "&omicron;" }, // greek small letter omicron, U+03BF NEW -->
+			{ "\u03C0", "&pi;" }, // greek small letter pi, U+03C0 ISOgrk3 -->
+			{ "\u03C1", "&rho;" }, // greek small letter rho, U+03C1 ISOgrk3 -->
+			{ "\u03C2", "&sigmaf;" }, // greek small letter final sigma,U+03C2 ISOgrk3 -->
+			{ "\u03C3", "&sigma;" }, // greek small letter sigma,U+03C3 ISOgrk3 -->
+			{ "\u03C4", "&tau;" }, // greek small letter tau, U+03C4 ISOgrk3 -->
+			{ "\u03C5", "&upsilon;" }, // greek small letter upsilon,U+03C5 ISOgrk3 -->
+			{ "\u03C6", "&phi;" }, // greek small letter phi, U+03C6 ISOgrk3 -->
+			{ "\u03C7", "&chi;" }, // greek small letter chi, U+03C7 ISOgrk3 -->
+			{ "\u03C8", "&psi;" }, // greek small letter psi, U+03C8 ISOgrk3 -->
+			{ "\u03C9", "&omega;" }, // greek small letter omega,U+03C9 ISOgrk3 -->
+			{ "\u03D1", "&thetasym;" }, // greek small letter theta symbol,U+03D1 NEW -->
+			{ "\u03D2", "&upsih;" }, // greek upsilon with hook symbol,U+03D2 NEW -->
+			{ "\u03D6", "&piv;" }, // greek pi symbol, U+03D6 ISOgrk3 -->
+			// <!-- General Punctuation -->
+			{ "\u2022", "&bull;" }, // bullet = black small circle,U+2022 ISOpub -->
+			// <!-- bullet is NOT the same as bullet operator, U+2219 -->
+			{ "\u2026", "&hellip;" }, // horizontal ellipsis = three dot leader,U+2026 ISOpub -->
+			{ "\u2032", "&prime;" }, // prime = minutes = feet, U+2032 ISOtech -->
+			{ "\u2033", "&Prime;" }, // double prime = seconds = inches,U+2033 ISOtech -->
+			{ "\u203E", "&oline;" }, // overline = spacing overscore,U+203E NEW -->
+			{ "\u2044", "&frasl;" }, // fraction slash, U+2044 NEW -->
+			// <!-- Letterlike Symbols -->
+			{ "\u2118", "&weierp;" }, // script capital P = power set= Weierstrass p, U+2118 ISOamso -->
+			{ "\u2111", "&image;" }, // blackletter capital I = imaginary part,U+2111 ISOamso -->
+			{ "\u211C", "&real;" }, // blackletter capital R = real part symbol,U+211C ISOamso -->
+			{ "\u2122", "&trade;" }, // trade mark sign, U+2122 ISOnum -->
+			{ "\u2135", "&alefsym;" }, // alef symbol = first transfinite cardinal,U+2135 NEW -->
+			// <!-- alef symbol is NOT the same as hebrew letter alef,U+05D0 although the
+			// same glyph could be used to depict both characters -->
+			// <!-- Arrows -->
+			{ "\u2190", "&larr;" }, // leftwards arrow, U+2190 ISOnum -->
+			{ "\u2191", "&uarr;" }, // upwards arrow, U+2191 ISOnum-->
+			{ "\u2192", "&rarr;" }, // rightwards arrow, U+2192 ISOnum -->
+			{ "\u2193", "&darr;" }, // downwards arrow, U+2193 ISOnum -->
+			{ "\u2194", "&harr;" }, // left right arrow, U+2194 ISOamsa -->
+			{ "\u21B5", "&crarr;" }, // downwards arrow with corner leftwards= carriage return, U+21B5 NEW -->
+			{ "\u21D0", "&lArr;" }, // leftwards double arrow, U+21D0 ISOtech -->
+			// <!-- ISO 10646 does not say that lArr is the same as the 'is implied by'
+			// arrow but also does not have any other character for that function.
+			// So ? lArr canbe used for 'is implied by' as ISOtech suggests -->
+			{ "\u21D1", "&uArr;" }, // upwards double arrow, U+21D1 ISOamsa -->
+			{ "\u21D2", "&rArr;" }, // rightwards double arrow,U+21D2 ISOtech -->
+			// <!-- ISO 10646 does not say this is the 'implies' character but does not
+			// have another character with this function so ?rArr can be used for
+			// 'implies' as ISOtech suggests -->
+			{ "\u21D3", "&dArr;" }, // downwards double arrow, U+21D3 ISOamsa -->
+			{ "\u21D4", "&hArr;" }, // left right double arrow,U+21D4 ISOamsa -->
+			// <!-- Mathematical Operators -->
+			{ "\u2200", "&forall;" }, // for all, U+2200 ISOtech -->
+			{ "\u2202", "&part;" }, // partial differential, U+2202 ISOtech -->
+			{ "\u2203", "&exist;" }, // there exists, U+2203 ISOtech -->
+			{ "\u2205", "&empty;" }, // empty set = null set = diameter,U+2205 ISOamso -->
+			{ "\u2207", "&nabla;" }, // nabla = backward difference,U+2207 ISOtech -->
+			{ "\u2208", "&isin;" }, // element of, U+2208 ISOtech -->
+			{ "\u2209", "&notin;" }, // not an element of, U+2209 ISOtech -->
+			{ "\u220B", "&ni;" }, // contains as member, U+220B ISOtech -->
+			// <!-- should there be a more memorable name than 'ni'? -->
+			{ "\u220F", "&prod;" }, // n-ary product = product sign,U+220F ISOamsb -->
+			// <!-- prod is NOT the same character as U+03A0 'greek capital letter pi'
+			// though the same glyph might be used for both -->
+			{ "\u2211", "&sum;" }, // n-ary summation, U+2211 ISOamsb -->
+			// <!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
+			// though the same glyph might be used for both -->
+			{ "\u2212", "&minus;" }, // minus sign, U+2212 ISOtech -->
+			{ "\u2217", "&lowast;" }, // asterisk operator, U+2217 ISOtech -->
+			{ "\u221A", "&radic;" }, // square root = radical sign,U+221A ISOtech -->
+			{ "\u221D", "&prop;" }, // proportional to, U+221D ISOtech -->
+			{ "\u221E", "&infin;" }, // infinity, U+221E ISOtech -->
+			{ "\u2220", "&ang;" }, // angle, U+2220 ISOamso -->
+			{ "\u2227", "&and;" }, // logical and = wedge, U+2227 ISOtech -->
+			{ "\u2228", "&or;" }, // logical or = vee, U+2228 ISOtech -->
+			{ "\u2229", "&cap;" }, // intersection = cap, U+2229 ISOtech -->
+			{ "\u222A", "&cup;" }, // union = cup, U+222A ISOtech -->
+			{ "\u222B", "&int;" }, // integral, U+222B ISOtech -->
+			{ "\u2234", "&there4;" }, // therefore, U+2234 ISOtech -->
+			{ "\u223C", "&sim;" }, // tilde operator = varies with = similar to,U+223C ISOtech -->
+			// <!-- tilde operator is NOT the same character as the tilde, U+007E,although
+			// the same glyph might be used to represent both -->
+			{ "\u2245", "&cong;" }, // approximately equal to, U+2245 ISOtech -->
+			{ "\u2248", "&asymp;" }, // almost equal to = asymptotic to,U+2248 ISOamsr -->
+			{ "\u2260", "&ne;" }, // not equal to, U+2260 ISOtech -->
+			{ "\u2261", "&equiv;" }, // identical to, U+2261 ISOtech -->
+			{ "\u2264", "&le;" }, // less-than or equal to, U+2264 ISOtech -->
+			{ "\u2265", "&ge;" }, // greater-than or equal to,U+2265 ISOtech -->
+			{ "\u2282", "&sub;" }, // subset of, U+2282 ISOtech -->
+			{ "\u2283", "&sup;" }, // superset of, U+2283 ISOtech -->
+			// <!-- note that nsup, 'not a superset of, U+2283' is not covered by the
+			// Symbol font encoding and is not included. Should it be, for symmetry?
+			// It is in ISOamsn --> <!ENTITY nsub", "8836"},
+			// not a subset of, U+2284 ISOamsn -->
+			{ "\u2286", "&sube;" }, // subset of or equal to, U+2286 ISOtech -->
+			{ "\u2287", "&supe;" }, // superset of or equal to,U+2287 ISOtech -->
+			{ "\u2295", "&oplus;" }, // circled plus = direct sum,U+2295 ISOamsb -->
+			{ "\u2297", "&otimes;" }, // circled times = vector product,U+2297 ISOamsb -->
+			{ "\u22A5", "&perp;" }, // up tack = orthogonal to = perpendicular,U+22A5 ISOtech -->
+			{ "\u22C5", "&sdot;" }, // dot operator, U+22C5 ISOamsb -->
+			// <!-- dot operator is NOT the same character as U+00B7 middle dot -->
+			// <!-- Miscellaneous Technical -->
+			{ "\u2308", "&lceil;" }, // left ceiling = apl upstile,U+2308 ISOamsc -->
+			{ "\u2309", "&rceil;" }, // right ceiling, U+2309 ISOamsc -->
+			{ "\u230A", "&lfloor;" }, // left floor = apl downstile,U+230A ISOamsc -->
+			{ "\u230B", "&rfloor;" }, // right floor, U+230B ISOamsc -->
+			{ "\u2329", "&lang;" }, // left-pointing angle bracket = bra,U+2329 ISOtech -->
+			// <!-- lang is NOT the same character as U+003C 'less than' or U+2039 'single
+			// left-pointing angle quotation
+			// mark' -->
+			{ "\u232A", "&rang;" }, // right-pointing angle bracket = ket,U+232A ISOtech -->
+			// <!-- rang is NOT the same character as U+003E 'greater than' or U+203A
+			// 'single right-pointing angle quotation mark' -->
+			// <!-- Geometric Shapes -->
+			{ "\u25CA", "&loz;" }, // lozenge, U+25CA ISOpub -->
+			// <!-- Miscellaneous Symbols -->
+			{ "\u2660", "&spades;" }, // black spade suit, U+2660 ISOpub -->
+			// <!-- black here seems to mean filled as opposed to hollow -->
+			{ "\u2663", "&clubs;" }, // black club suit = shamrock,U+2663 ISOpub -->
+			{ "\u2665", "&hearts;" }, // black heart suit = valentine,U+2665 ISOpub -->
+			{ "\u2666", "&diams;" }, // black diamond suit, U+2666 ISOpub -->
+
+			// <!-- Latin Extended-A -->
+			{ "\u0152", "&OElig;" }, // -- latin capital ligature OE,U+0152 ISOlat2 -->
+			{ "\u0153", "&oelig;" }, // -- latin small ligature oe, U+0153 ISOlat2 -->
+			// <!-- ligature is a misnomer, this is a separate character in some languages
+			// -->
+			{ "\u0160", "&Scaron;" }, // -- latin capital letter S with caron,U+0160 ISOlat2 -->
+			{ "\u0161", "&scaron;" }, // -- latin small letter s with caron,U+0161 ISOlat2 -->
+			{ "\u0178", "&Yuml;" }, // -- latin capital letter Y with diaeresis,U+0178 ISOlat2 -->
+			// <!-- Spacing Modifier Letters -->
+			{ "\u02C6", "&circ;" }, // -- modifier letter circumflex accent,U+02C6 ISOpub -->
+			{ "\u02DC", "&tilde;" }, // small tilde, U+02DC ISOdia -->
+			// <!-- General Punctuation -->
+			{ "\u2002", "&ensp;" }, // en space, U+2002 ISOpub -->
+			{ "\u2003", "&emsp;" }, // em space, U+2003 ISOpub -->
+			{ "\u2009", "&thinsp;" }, // thin space, U+2009 ISOpub -->
+			{ "\u200C", "&zwnj;" }, // zero width non-joiner,U+200C NEW RFC 2070 -->
+			{ "\u200D", "&zwj;" }, // zero width joiner, U+200D NEW RFC 2070 -->
+			{ "\u200E", "&lrm;" }, // left-to-right mark, U+200E NEW RFC 2070 -->
+			{ "\u200F", "&rlm;" }, // right-to-left mark, U+200F NEW RFC 2070 -->
+			{ "\u2013", "&ndash;" }, // en dash, U+2013 ISOpub -->
+			{ "\u2014", "&mdash;" }, // em dash, U+2014 ISOpub -->
+			{ "\u2018", "&lsquo;" }, // left single quotation mark,U+2018 ISOnum -->
+			{ "\u2019", "&rsquo;" }, // right single quotation mark,U+2019 ISOnum -->
+			{ "\u201A", "&sbquo;" }, // single low-9 quotation mark, U+201A NEW -->
+			{ "\u201C", "&ldquo;" }, // left double quotation mark,U+201C ISOnum -->
+			{ "\u201D", "&rdquo;" }, // right double quotation mark,U+201D ISOnum -->
+			{ "\u201E", "&bdquo;" }, // double low-9 quotation mark, U+201E NEW -->
+			{ "\u2020", "&dagger;" }, // dagger, U+2020 ISOpub -->
+			{ "\u2021", "&Dagger;" }, // double dagger, U+2021 ISOpub -->
+			{ "\u2030", "&permil;" }, // per mille sign, U+2030 ISOtech -->
+			{ "\u2039", "&lsaquo;" }, // single left-pointing angle quotation mark,U+2039 ISO proposed -->
+			// <!-- lsaquo is proposed but not yet ISO standardized -->
+			{ "\u203A", "&rsaquo;" }, // single right-pointing angle quotation mark,U+203A ISO proposed -->
+			// <!-- rsaquo is proposed but not yet ISO standardized -->
+			{ "\u20AC", "&euro;" }, // -- euro sign, U+20AC NEW -->
+	};
+
+	/**
+	 * Reverse of {@link #HTML40_EXTENDED_ESCAPE()} for unescaping purposes.
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] HTML40_EXTENDED_UNESCAPE() {
+		return HTML40_EXTENDED_UNESCAPE.clone();
+	}
+
+	private static final String[][] HTML40_EXTENDED_UNESCAPE = invert(HTML40_EXTENDED_ESCAPE);
+
+	/**
+	 * Mapping to escape the basic XML and HTML character entities.
+	 *
+	 * Namely: {@code " & < >}
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] BASIC_ESCAPE() {
+		return BASIC_ESCAPE.clone();
+	}
+
+	private static final String[][] BASIC_ESCAPE = { { "\"", "&quot;" }, // " - double-quote
+			{ "&", "&amp;" }, // & - ampersand
+			{ "<", "&lt;" }, // < - less-than
+			{ ">", "&gt;" }, // > - greater-than
+	};
+
+	/**
+	 * Reverse of {@link #BASIC_ESCAPE()} for unescaping purposes.
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] BASIC_UNESCAPE() {
+		return BASIC_UNESCAPE.clone();
+	}
+
+	private static final String[][] BASIC_UNESCAPE = invert(BASIC_ESCAPE);
+
+	/**
+	 * Mapping to escape the apostrophe character to its XML character entity.
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] APOS_ESCAPE() {
+		return APOS_ESCAPE.clone();
+	}
+
+	private static final String[][] APOS_ESCAPE = { { "'", "&apos;" }, // XML apostrophe
+	};
+
+	/**
+	 * Reverse of {@link #APOS_ESCAPE()} for unescaping purposes.
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] APOS_UNESCAPE() {
+		return APOS_UNESCAPE.clone();
+	}
+
+	private static final String[][] APOS_UNESCAPE = invert(APOS_ESCAPE);
+
+	/**
+	 * Mapping to escape the Java control characters.
+	 *
+	 * Namely: {@code \b \n \t \f \r}
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] JAVA_CTRL_CHARS_ESCAPE() {
+		return JAVA_CTRL_CHARS_ESCAPE.clone();
+	}
+
+	private static final String[][] JAVA_CTRL_CHARS_ESCAPE = { { "\b", "\\b" }, { "\n", "\\n" }, { "\t", "\\t" },
+			{ "\f", "\\f" }, { "\r", "\\r" } };
+
+	/**
+	 * Reverse of {@link #JAVA_CTRL_CHARS_ESCAPE()} for unescaping purposes.
+	 * 
+	 * @return the mapping table
+	 */
+	public static String[][] JAVA_CTRL_CHARS_UNESCAPE() {
+		return JAVA_CTRL_CHARS_UNESCAPE.clone();
+	}
+
+	private static final String[][] JAVA_CTRL_CHARS_UNESCAPE = invert(JAVA_CTRL_CHARS_ESCAPE);
+
+	/**
+	 * Used to invert an escape array into an unescape array
+	 * 
+	 * @param array String[][] to be inverted
+	 * @return String[][] inverted array
+	 */
+	public static String[][] invert(final String[][] array) {
+		final String[][] newarray = new String[array.length][2];
+		for (int i = 0; i < array.length; i++) {
+			newarray[i][0] = array[i][1];
+			newarray[i][1] = array[i][0];
+		}
+		return newarray;
+	}
+
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/JavaUnicodeEscaper.java b/sources/main/java/org/apache/commons/lang3/text/translate/JavaUnicodeEscaper.java
new file mode 100644
index 00000000..8944c016
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/JavaUnicodeEscaper.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+/**
+ * Translates codepoints to their Unicode escaped value suitable for Java
+ * source.
+ * 
+ * @since 3.2
+ * @version $Id: JavaUnicodeEscaper.java 1451550 2013-03-01 10:06:13Z olamy $
+ */
+public class JavaUnicodeEscaper extends UnicodeEscaper {
+
+	/**
+	 * <p>
+	 * Constructs a <code>JavaUnicodeEscaper</code> above the specified value
+	 * (exclusive).
+	 * </p>
+	 * 
+	 * @param codepoint above which to escape
+	 * @return the newly created {@code UnicodeEscaper} instance
+	 */
+	public static JavaUnicodeEscaper above(final int codepoint) {
+		return outsideOf(0, codepoint);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>JavaUnicodeEscaper</code> below the specified value
+	 * (exclusive).
+	 * </p>
+	 * 
+	 * @param codepoint below which to escape
+	 * @return the newly created {@code UnicodeEscaper} instance
+	 */
+	public static JavaUnicodeEscaper below(final int codepoint) {
+		return outsideOf(codepoint, Integer.MAX_VALUE);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>JavaUnicodeEscaper</code> between the specified values
+	 * (inclusive).
+	 * </p>
+	 * 
+	 * @param codepointLow  above which to escape
+	 * @param codepointHigh below which to escape
+	 * @return the newly created {@code UnicodeEscaper} instance
+	 */
+	public static JavaUnicodeEscaper between(final int codepointLow, final int codepointHigh) {
+		return new JavaUnicodeEscaper(codepointLow, codepointHigh, true);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>JavaUnicodeEscaper</code> outside of the specified values
+	 * (exclusive).
+	 * </p>
+	 * 
+	 * @param codepointLow  below which to escape
+	 * @param codepointHigh above which to escape
+	 * @return the newly created {@code UnicodeEscaper} instance
+	 */
+	public static JavaUnicodeEscaper outsideOf(final int codepointLow, final int codepointHigh) {
+		return new JavaUnicodeEscaper(codepointLow, codepointHigh, false);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>JavaUnicodeEscaper</code> for the specified range. This is
+	 * the underlying method for the other constructors/builders. The
+	 * <code>below</code> and <code>above</code> boundaries are inclusive when
+	 * <code>between</code> is <code>true</code> and exclusive when it is
+	 * <code>false</code>.
+	 * </p>
+	 * 
+	 * @param below   int value representing the lowest codepoint boundary
+	 * @param above   int value representing the highest codepoint boundary
+	 * @param between whether to escape between the boundaries or outside them
+	 */
+	public JavaUnicodeEscaper(final int below, final int above, final boolean between) {
+		super(below, above, between);
+	}
+
+	/**
+	 * Converts the given codepoint to a hex string of the form
+	 * {@code "\\uXXXX\\uXXXX"}
+	 * 
+	 * @param codepoint a Unicode code point
+	 * @return the hex string for the given codepoint
+	 */
+	@Override
+	protected String toUtf16Escape(final int codepoint) {
+		final char[] surrogatePair = Character.toChars(codepoint);
+		return "\\u" + hex(surrogatePair[0]) + "\\u" + hex(surrogatePair[1]);
+	}
+
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/LookupTranslator.java b/sources/main/java/org/apache/commons/lang3/text/translate/LookupTranslator.java
new file mode 100644
index 00000000..e3c93345
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/LookupTranslator.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+
+/**
+ * Translates a value using a lookup table.
+ *
+ * @since 3.0
+ * @version $Id: LookupTranslator.java 1470822 2013-04-23 06:00:41Z bayard $
+ */
+public class LookupTranslator extends CharSequenceTranslator {
+
+	private final HashMap<String, CharSequence> lookupMap;
+	private final int shortest;
+	private final int longest;
+
+	/**
+	 * Define the lookup table to be used in translation
+	 *
+	 * Note that, as of Lang 3.1, the key to the lookup table is converted to a
+	 * java.lang.String, while the value remains as a java.lang.CharSequence. This
+	 * is because we need the key to support hashCode and equals(Object), allowing
+	 * it to be the key for a HashMap. See LANG-882.
+	 *
+	 * @param lookup CharSequence[][] table of size [*][2]
+	 */
+	public LookupTranslator(final CharSequence[]... lookup) {
+		lookupMap = new HashMap<String, CharSequence>();
+		int _shortest = Integer.MAX_VALUE;
+		int _longest = 0;
+		if (lookup != null) {
+			for (final CharSequence[] seq : lookup) {
+				this.lookupMap.put(seq[0].toString(), seq[1]);
+				final int sz = seq[0].length();
+				if (sz < _shortest) {
+					_shortest = sz;
+				}
+				if (sz > _longest) {
+					_longest = sz;
+				}
+			}
+		}
+		shortest = _shortest;
+		longest = _longest;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
+		int max = longest;
+		if (index + longest > input.length()) {
+			max = input.length() - index;
+		}
+		// descend so as to get a greedy algorithm
+		for (int i = max; i >= shortest; i--) {
+			final CharSequence subSeq = input.subSequence(index, index + i);
+			final CharSequence result = lookupMap.get(subSeq.toString());
+			if (result != null) {
+				out.write(result.toString());
+				return i;
+			}
+		}
+		return 0;
+	}
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java b/sources/main/java/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java
new file mode 100644
index 00000000..b216c0c3
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Translates codepoints to their XML numeric entity escaped value.
+ *
+ * @since 3.0
+ * @version $Id: NumericEntityEscaper.java 1436768 2013-01-22 07:07:42Z ggregory
+ *          $
+ */
+public class NumericEntityEscaper extends CodePointTranslator {
+
+	private final int below;
+	private final int above;
+	private final boolean between;
+
+	/**
+	 * <p>
+	 * Constructs a <code>NumericEntityEscaper</code> for the specified range. This
+	 * is the underlying method for the other constructors/builders. The
+	 * <code>below</code> and <code>above</code> boundaries are inclusive when
+	 * <code>between</code> is <code>true</code> and exclusive when it is
+	 * <code>false</code>.
+	 * </p>
+	 *
+	 * @param below   int value representing the lowest codepoint boundary
+	 * @param above   int value representing the highest codepoint boundary
+	 * @param between whether to escape between the boundaries or outside them
+	 */
+	private NumericEntityEscaper(final int below, final int above, final boolean between) {
+		this.below = below;
+		this.above = above;
+		this.between = between;
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>NumericEntityEscaper</code> for all characters.
+	 * </p>
+	 */
+	public NumericEntityEscaper() {
+		this(0, Integer.MAX_VALUE, true);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>NumericEntityEscaper</code> below the specified value
+	 * (exclusive).
+	 * </p>
+	 *
+	 * @param codepoint below which to escape
+	 * @return the newly created {@code NumericEntityEscaper} instance
+	 */
+	public static NumericEntityEscaper below(final int codepoint) {
+		return outsideOf(codepoint, Integer.MAX_VALUE);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>NumericEntityEscaper</code> above the specified value
+	 * (exclusive).
+	 * </p>
+	 *
+	 * @param codepoint above which to escape
+	 * @return the newly created {@code NumericEntityEscaper} instance
+	 */
+	public static NumericEntityEscaper above(final int codepoint) {
+		return outsideOf(0, codepoint);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>NumericEntityEscaper</code> between the specified values
+	 * (inclusive).
+	 * </p>
+	 *
+	 * @param codepointLow  above which to escape
+	 * @param codepointHigh below which to escape
+	 * @return the newly created {@code NumericEntityEscaper} instance
+	 */
+	public static NumericEntityEscaper between(final int codepointLow, final int codepointHigh) {
+		return new NumericEntityEscaper(codepointLow, codepointHigh, true);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>NumericEntityEscaper</code> outside of the specified
+	 * values (exclusive).
+	 * </p>
+	 *
+	 * @param codepointLow  below which to escape
+	 * @param codepointHigh above which to escape
+	 * @return the newly created {@code NumericEntityEscaper} instance
+	 */
+	public static NumericEntityEscaper outsideOf(final int codepointLow, final int codepointHigh) {
+		return new NumericEntityEscaper(codepointLow, codepointHigh, false);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean translate(final int codepoint, final Writer out) throws IOException {
+		if (between) {
+			if (codepoint < below || codepoint > above) {
+				return false;
+			}
+		} else {
+			if (codepoint >= below && codepoint <= above) {
+				return false;
+			}
+		}
+
+		out.write("&#");
+		out.write(Integer.toString(codepoint, 10));
+		out.write(';');
+		return true;
+	}
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java b/sources/main/java/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java
new file mode 100644
index 00000000..a6fe2945
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.EnumSet;
+
+/**
+ * Translate XML numeric entities of the form &amp;#[xX]?\d+;? to the specific
+ * codepoint.
+ *
+ * Note that the semi-colon is optional.
+ * 
+ * @since 3.0
+ * @version $Id: NumericEntityUnescaper.java 1583482 2014-03-31 22:54:57Z niallp
+ *          $
+ */
+public class NumericEntityUnescaper extends CharSequenceTranslator {
+
+	public static enum OPTION {
+		semiColonRequired, semiColonOptional, errorIfNoSemiColon
+	}
+
+	// TODO?: Create an OptionsSet class to hide some of the conditional logic below
+	private final EnumSet<OPTION> options;
+
+	/**
+	 * Create a UnicodeUnescaper.
+	 *
+	 * The constructor takes a list of options, only one type of which is currently
+	 * available (whether to allow, error or ignore the semi-colon on the end of a
+	 * numeric entity to being missing).
+	 *
+	 * For example, to support numeric entities without a ';': new
+	 * NumericEntityUnescaper(NumericEntityUnescaper.OPTION.semiColonOptional) and
+	 * to throw an IllegalArgumentException when they're missing: new
+	 * NumericEntityUnescaper(NumericEntityUnescaper.OPTION.errorIfNoSemiColon)
+	 *
+	 * Note that the default behaviour is to ignore them.
+	 *
+	 * @param options to apply to this unescaper
+	 */
+	public NumericEntityUnescaper(final OPTION... options) {
+		if (options.length > 0) {
+			this.options = EnumSet.copyOf(Arrays.asList(options));
+		} else {
+			this.options = EnumSet.copyOf(Arrays.asList(new OPTION[] { OPTION.semiColonRequired }));
+		}
+	}
+
+	/**
+	 * Whether the passed in option is currently set.
+	 *
+	 * @param option to check state of
+	 * @return whether the option is set
+	 */
+	public boolean isSet(final OPTION option) {
+		return options == null ? false : options.contains(option);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
+		final int seqEnd = input.length();
+		// Uses -2 to ensure there is something after the &#
+		if (input.charAt(index) == '&' && index < seqEnd - 2 && input.charAt(index + 1) == '#') {
+			int start = index + 2;
+			boolean isHex = false;
+
+			final char firstChar = input.charAt(start);
+			if (firstChar == 'x' || firstChar == 'X') {
+				start++;
+				isHex = true;
+
+				// Check there's more than just an x after the &#
+				if (start == seqEnd) {
+					return 0;
+				}
+			}
+
+			int end = start;
+			// Note that this supports character codes without a ; on the end
+			while (end < seqEnd && (input.charAt(end) >= '0' && input.charAt(end) <= '9'
+					|| input.charAt(end) >= 'a' && input.charAt(end) <= 'f'
+					|| input.charAt(end) >= 'A' && input.charAt(end) <= 'F')) {
+				end++;
+			}
+
+			final boolean semiNext = end != seqEnd && input.charAt(end) == ';';
+
+			if (!semiNext) {
+				if (isSet(OPTION.semiColonRequired)) {
+					return 0;
+				} else if (isSet(OPTION.errorIfNoSemiColon)) {
+					throw new IllegalArgumentException("Semi-colon required at end of numeric entity");
+				}
+			}
+
+			int entityValue;
+			try {
+				if (isHex) {
+					entityValue = Integer.parseInt(input.subSequence(start, end).toString(), 16);
+				} else {
+					entityValue = Integer.parseInt(input.subSequence(start, end).toString(), 10);
+				}
+			} catch (final NumberFormatException nfe) {
+				return 0;
+			}
+
+			if (entityValue > 0xFFFF) {
+				final char[] chrs = Character.toChars(entityValue);
+				out.write(chrs[0]);
+				out.write(chrs[1]);
+			} else {
+				out.write(entityValue);
+			}
+
+			return 2 + end - start + (isHex ? 1 : 0) + (semiNext ? 1 : 0);
+		}
+		return 0;
+	}
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/OctalUnescaper.java b/sources/main/java/org/apache/commons/lang3/text/translate/OctalUnescaper.java
new file mode 100644
index 00000000..7b041947
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/OctalUnescaper.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Translate escaped octal Strings back to their octal values.
+ *
+ * For example, "\45" should go back to being the specific value (a %).
+ *
+ * Note that this currently only supports the viable range of octal for Java;
+ * namely 1 to 377. This is because parsing Java is the main use case.
+ * 
+ * @since 3.0
+ * @version $Id: OctalUnescaper.java 967237 2010-07-23 20:08:57Z mbenson $
+ */
+public class OctalUnescaper extends CharSequenceTranslator {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
+		int remaining = input.length() - index - 1; // how many characters left, ignoring the first \
+		StringBuilder builder = new StringBuilder();
+		if (input.charAt(index) == '\\' && remaining > 0 && isOctalDigit(input.charAt(index + 1))) {
+			int next = index + 1;
+			int next2 = index + 2;
+			int next3 = index + 3;
+
+			// we know this is good as we checked it in the if block above
+			builder.append(input.charAt(next));
+
+			if (remaining > 1 && isOctalDigit(input.charAt(next2))) {
+				builder.append(input.charAt(next2));
+				if (remaining > 2 && isZeroToThree(input.charAt(next)) && isOctalDigit(input.charAt(next3))) {
+					builder.append(input.charAt(next3));
+				}
+			}
+
+			out.write(Integer.parseInt(builder.toString(), 8));
+			return 1 + builder.length();
+		}
+		return 0;
+	}
+
+	/**
+	 * Checks if the given char is an octal digit. Octal digits are the character
+	 * representations of the digits 0 to 7.
+	 * 
+	 * @param ch the char to check
+	 * @return true if the given char is the character representation of one of the
+	 *         digits from 0 to 7
+	 */
+	private boolean isOctalDigit(char ch) {
+		return ch >= '0' && ch <= '7';
+	}
+
+	/**
+	 * Checks if the given char is the character representation of one of the digit
+	 * from 0 to 3.
+	 * 
+	 * @param ch the char to check
+	 * @return true if the given char is the character representation of one of the
+	 *         digits from 0 to 3
+	 */
+	private boolean isZeroToThree(char ch) {
+		return ch >= '0' && ch <= '3';
+	}
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeEscaper.java b/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeEscaper.java
new file mode 100644
index 00000000..1aaf54b6
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeEscaper.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Translates codepoints to their Unicode escaped value.
+ *
+ * @since 3.0
+ * @version $Id: UnicodeEscaper.java 1552652 2013-12-20 13:23:16Z britter $
+ */
+public class UnicodeEscaper extends CodePointTranslator {
+
+	private final int below;
+	private final int above;
+	private final boolean between;
+
+	/**
+	 * <p>
+	 * Constructs a <code>UnicodeEscaper</code> for all characters.
+	 * </p>
+	 */
+	public UnicodeEscaper() {
+		this(0, Integer.MAX_VALUE, true);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>UnicodeEscaper</code> for the specified range. This is the
+	 * underlying method for the other constructors/builders. The <code>below</code>
+	 * and <code>above</code> boundaries are inclusive when <code>between</code> is
+	 * <code>true</code> and exclusive when it is <code>false</code>.
+	 * </p>
+	 *
+	 * @param below   int value representing the lowest codepoint boundary
+	 * @param above   int value representing the highest codepoint boundary
+	 * @param between whether to escape between the boundaries or outside them
+	 */
+	protected UnicodeEscaper(final int below, final int above, final boolean between) {
+		this.below = below;
+		this.above = above;
+		this.between = between;
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>UnicodeEscaper</code> below the specified value
+	 * (exclusive).
+	 * </p>
+	 *
+	 * @param codepoint below which to escape
+	 * @return the newly created {@code UnicodeEscaper} instance
+	 */
+	public static UnicodeEscaper below(final int codepoint) {
+		return outsideOf(codepoint, Integer.MAX_VALUE);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>UnicodeEscaper</code> above the specified value
+	 * (exclusive).
+	 * </p>
+	 *
+	 * @param codepoint above which to escape
+	 * @return the newly created {@code UnicodeEscaper} instance
+	 */
+	public static UnicodeEscaper above(final int codepoint) {
+		return outsideOf(0, codepoint);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>UnicodeEscaper</code> outside of the specified values
+	 * (exclusive).
+	 * </p>
+	 *
+	 * @param codepointLow  below which to escape
+	 * @param codepointHigh above which to escape
+	 * @return the newly created {@code UnicodeEscaper} instance
+	 */
+	public static UnicodeEscaper outsideOf(final int codepointLow, final int codepointHigh) {
+		return new UnicodeEscaper(codepointLow, codepointHigh, false);
+	}
+
+	/**
+	 * <p>
+	 * Constructs a <code>UnicodeEscaper</code> between the specified values
+	 * (inclusive).
+	 * </p>
+	 *
+	 * @param codepointLow  above which to escape
+	 * @param codepointHigh below which to escape
+	 * @return the newly created {@code UnicodeEscaper} instance
+	 */
+	public static UnicodeEscaper between(final int codepointLow, final int codepointHigh) {
+		return new UnicodeEscaper(codepointLow, codepointHigh, true);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean translate(final int codepoint, final Writer out) throws IOException {
+		if (between) {
+			if (codepoint < below || codepoint > above) {
+				return false;
+			}
+		} else {
+			if (codepoint >= below && codepoint <= above) {
+				return false;
+			}
+		}
+
+		// TODO: Handle potential + sign per various Unicode escape implementations
+		if (codepoint > 0xffff) {
+			out.write(toUtf16Escape(codepoint));
+		} else if (codepoint > 0xfff) {
+			out.write("\\u" + hex(codepoint));
+		} else if (codepoint > 0xff) {
+			out.write("\\u0" + hex(codepoint));
+		} else if (codepoint > 0xf) {
+			out.write("\\u00" + hex(codepoint));
+		} else {
+			out.write("\\u000" + hex(codepoint));
+		}
+		return true;
+	}
+
+	/**
+	 * Converts the given codepoint to a hex string of the form {@code "\\uXXXX"}
+	 * 
+	 * @param codepoint a Unicode code point
+	 * @return the hex string for the given codepoint
+	 *
+	 * @since 3.2
+	 */
+	protected String toUtf16Escape(final int codepoint) {
+		return "\\u" + hex(codepoint);
+	}
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeUnescaper.java b/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeUnescaper.java
new file mode 100644
index 00000000..5881d12c
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeUnescaper.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Translates escaped Unicode values of the form \\u+\d\d\d\d back to Unicode.
+ * It supports multiple 'u' characters and will work with or without the +.
+ * 
+ * @since 3.0
+ * @version $Id: UnicodeUnescaper.java 1436770 2013-01-22 07:09:45Z ggregory $
+ */
+public class UnicodeUnescaper extends CharSequenceTranslator {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
+		if (input.charAt(index) == '\\' && index + 1 < input.length() && input.charAt(index + 1) == 'u') {
+			// consume optional additional 'u' chars
+			int i = 2;
+			while (index + i < input.length() && input.charAt(index + i) == 'u') {
+				i++;
+			}
+
+			if (index + i < input.length() && input.charAt(index + i) == '+') {
+				i++;
+			}
+
+			if (index + i + 4 <= input.length()) {
+				// Get 4 hex digits
+				final CharSequence unicode = input.subSequence(index + i, index + i + 4);
+
+				try {
+					final int value = Integer.parseInt(unicode.toString(), 16);
+					out.write((char) value);
+				} catch (final NumberFormatException nfe) {
+					throw new IllegalArgumentException("Unable to parse unicode value: " + unicode, nfe);
+				}
+				return i + 4;
+			} else {
+				throw new IllegalArgumentException("Less than 4 hex digits in unicode value: '"
+						+ input.subSequence(index, input.length()) + "' due to end of CharSequence");
+			}
+		}
+		return 0;
+	}
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeUnpairedSurrogateRemover.java b/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeUnpairedSurrogateRemover.java
new file mode 100644
index 00000000..1ccf6fe8
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/UnicodeUnpairedSurrogateRemover.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.text.translate;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Helper subclass to CharSequenceTranslator to remove unpaired surrogates.
+ * 
+ * @version $Id: UnicodeUnpairedSurrogateRemover.java 1568639 2014-02-15
+ *          16:13:27Z britter $
+ */
+public class UnicodeUnpairedSurrogateRemover extends CodePointTranslator {
+	/**
+	 * Implementation of translate that throws out unpaired surrogates.
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean translate(int codepoint, Writer out) throws IOException {
+		if (codepoint >= Character.MIN_SURROGATE && codepoint <= Character.MAX_SURROGATE) {
+			// It's a surrogate. Write nothing and say we've translated.
+			return true;
+		} else {
+			// It's not a surrogate. Don't translate it.
+			return false;
+		}
+	}
+}
diff --git a/sources/main/java/org/apache/commons/lang3/text/translate/package-info.java b/sources/main/java/org/apache/commons/lang3/text/translate/package-info.java
new file mode 100644
index 00000000..58568d0b
--- /dev/null
+++ b/sources/main/java/org/apache/commons/lang3/text/translate/package-info.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * <p>
+ * An API for creating text translation routines from a set of smaller building
+ * blocks. Initially created to make it possible for the user to customize the
+ * rules in the StringEscapeUtils class.
+ * </p>
+ * <p>
+ * These classes are immutable, and therefore thread-safe.
+ * </p>
+ *
+ * @since 3.0
+ * @version $Id: package-info.java 1558546 2014-01-15 19:38:15Z britter $
+ */
+package org.apache.commons.lang3.text.translate;
diff --git a/sources/resources/EPKVersionIdentifier.txt b/sources/resources/EPKVersionIdentifier.txt
index 503c4b5b..ddc35e29 100644
--- a/sources/resources/EPKVersionIdentifier.txt
+++ b/sources/resources/EPKVersionIdentifier.txt
@@ -1 +1 @@
-u47
\ No newline at end of file
+u48
\ No newline at end of file
diff --git a/sources/resources/assets/eagler/CREDITS.txt b/sources/resources/assets/eagler/CREDITS.txt
index 6e709230..ac539ce5 100644
--- a/sources/resources/assets/eagler/CREDITS.txt
+++ b/sources/resources/assets/eagler/CREDITS.txt
@@ -239,6 +239,58 @@
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
+ Project Name: Alfheim
+ Project Author: Red Studio
+ Project URL: https://github.com/Red-Studio-Ragnarok/Alfheim
+ 
+ Used For: Optimized lighting engine
+ 
+ * MIT License
+ *
+ * Copyright (c) 2023 Red Studio
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ 
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ 
+ Project Name: OptiFine (1.8.9_HD_U_M6)
+ Project Author: sp614x
+ Project URL: https://optifine.net/
+ 
+ Used For: A few, limited portions we "borrowed" without the author's permission
+ 
+ Note: Eaglercraft is not OptiFine, we only use the code for connected textures
+       and maintaining compatibility with certain resource pack features
+ 
+ * The mod OptiFine is Copyright © 2011-2021 by sp614x and the intellectual
+ * property of the author.
+ * It may be not be reproduced under any circumstances except for personal,
+ * private use as long as it remains in its unaltered, unedited form.
+ * It may not be placed on any web site or otherwise distributed publicly
+ * without advance written permission.
+ * Use of this mod on any other website or as a part of any public display
+ * is strictly prohibited and a violation of copyright.
+ 
+ (sorry sp614x)
+ 
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ 
  Project Name: Google Guava
  Project Author: Google
  Project URL: https://github.com/google/guava
diff --git a/sources/resources/assets/eagler/glsl/deferred/deferred_combine.fsh b/sources/resources/assets/eagler/glsl/deferred/deferred_combine.fsh
index a55bea58..d9931aea 100644
--- a/sources/resources/assets/eagler/glsl/deferred/deferred_combine.fsh
+++ b/sources/resources/assets/eagler/glsl/deferred/deferred_combine.fsh
@@ -141,8 +141,9 @@ void main() {
 			reflectDir.xz += vec2(0.5, reflectDir.y > 0.0 ? 0.25 : 0.75);
 			envMapSample4f = textureLod(u_environmentMap, reflectDir.xz, 0.0);
 		}
-		if(envMapSample4f.a > 0.0) {
-			specular = eaglercraftIBL_Specular(diffuseColor3f.rgb, envMapSample4f.rgb, viewDir3f, normalVector3f, materialData4f.rgb);
+		envMapSample4f.a += min(lightmapCoords2f.g * 2.0, 1.0) * (1.0 - envMapSample4f.a);
+		if(envMapSample4f.a == 1.0) {
+			specular = eaglercraftIBL_Specular(diffuseColor3f.rgb, envMapSample4f.rgb * envMapSample4f.a, viewDir3f, normalVector3f, materialData4f.rgb);
 			specular *= 1.0 - sqrt(posDst) * 0.2;
 		}
 		break;
diff --git a/sources/resources/assets/eagler/glsl/deferred/forward_core.fsh b/sources/resources/assets/eagler/glsl/deferred/forward_core.fsh
index 65dbf97a..36e0acc0 100644
--- a/sources/resources/assets/eagler/glsl/deferred/forward_core.fsh
+++ b/sources/resources/assets/eagler/glsl/deferred/forward_core.fsh
@@ -21,7 +21,7 @@ precision highp sampler2DShadow;
 in vec4 v_position4f;
 
 #ifdef COMPILE_FOG_LIGHT_SHAFTS
-in vec2 v_positionClip2f;
+in vec3 v_positionClip3f;
 #endif
 
 #ifdef COMPILE_TEXTURE_ATTRIB
@@ -351,7 +351,7 @@ void main() {
 	f += materialData3f.r < 0.5 ? 1.0 : 0.0;
 	while(f == 0.0) {
 		float dst2 = dot(worldPosition4f.xyz, worldPosition4f.xyz);
-		if(dst2 > 16.0) {
+		if(dst2 > 25.0) {
 			break;
 		}
 		vec3 reflectDir = reflect(worldDirection4f.xyz, normalVector3f);
@@ -369,8 +369,9 @@ void main() {
 			reflectDir.xz += vec2(0.5, reflectDir.y > 0.0 ? 0.25 : 0.75);
 			envMapSample4f = textureLod(u_environmentMap, reflectDir.xz, 0.0);
 		}
-		if(envMapSample4f.a > 0.0) {
-			lightColor3f += eaglercraftIBL_Specular(diffuseColor4f.rgb, envMapSample4f.rgb, worldDirection4f.xyz, normalVector3f, materialData3f, metalN, metalK) * (1.0 - sqrt(dst2) * 0.25);
+		envMapSample4f.a += min(lightmapCoords2f.g * 2.0, 1.0) * (1.0 - envMapSample4f.a);
+		if(envMapSample4f.a == 1.0) {
+			lightColor3f += eaglercraftIBL_Specular(diffuseColor4f.rgb, envMapSample4f.rgb * envMapSample4f.a, worldDirection4f.xyz, normalVector3f, materialData3f, metalN, metalK) * (1.0 - sqrt(dst2) * 0.2);
 		}
 		break;
 	}
@@ -453,7 +454,7 @@ void main() {
 		fogBlend4f.rgb *= textureLod(u_irradianceMap, atmosSamplePos.xz, 0.0).rgb;
 
 #ifdef COMPILE_FOG_LIGHT_SHAFTS
-		fogBlend4f.rgb *= pow(textureLod(u_lightShaftsTexture, v_positionClip2f * 0.5 + 0.5, 0.0).r * 0.9 + 0.1, 2.25);
+		fogBlend4f.rgb *= pow(textureLod(u_lightShaftsTexture, (v_positionClip3f.xy / v_positionClip3f.z) * 0.5 + 0.5, 0.0).r * 0.9 + 0.1, 2.25);
 		fogBlend4f.a = fogBlend4f.a * 0.85 + 0.2;
 #endif
 		break;
diff --git a/sources/resources/assets/eagler/glsl/deferred/forward_core.vsh b/sources/resources/assets/eagler/glsl/deferred/forward_core.vsh
index 5859caf4..9052b9ce 100644
--- a/sources/resources/assets/eagler/glsl/deferred/forward_core.vsh
+++ b/sources/resources/assets/eagler/glsl/deferred/forward_core.vsh
@@ -21,7 +21,7 @@ in vec3 a_position3f;
 out vec4 v_position4f;
 
 #ifdef COMPILE_FOG_LIGHT_SHAFTS
-out vec2 v_positionClip2f;
+out vec3 v_positionClip3f;
 #endif
 
 #ifdef COMPILE_TEXTURE_ATTRIB
@@ -98,6 +98,6 @@ void main() {
 #endif
 
 #ifdef COMPILE_FOG_LIGHT_SHAFTS
-	v_positionClip2f = gl_Position.xy / gl_Position.w;
+	v_positionClip3f = gl_Position.xyw;
 #endif
 }
diff --git a/sources/resources/assets/eagler/glsl/deferred/forward_glass_highlights.fsh b/sources/resources/assets/eagler/glsl/deferred/forward_glass_highlights.fsh
index 51e12b37..76749b73 100644
--- a/sources/resources/assets/eagler/glsl/deferred/forward_glass_highlights.fsh
+++ b/sources/resources/assets/eagler/glsl/deferred/forward_glass_highlights.fsh
@@ -238,7 +238,7 @@ void main() {
 
 	for(;;) {
 		float dst2 = dot(worldPosition4f.xyz, worldPosition4f.xyz);
-		if(dst2 > 16.0) {
+		if(dst2 > 25.0) {
 			break;
 		}
 		vec3 reflectDir = reflect(worldDirection4f.xyz, normalVector3f);
@@ -256,8 +256,9 @@ void main() {
 			reflectDir.xz += vec2(0.5, reflectDir.y > 0.0 ? 0.25 : 0.75);
 			envMapSample4f = textureLod(u_environmentMap, reflectDir.xz, 0.0);
 		}
-		if(envMapSample4f.a > 0.0) {
-			lightColor3f += eaglercraftIBL_Specular_Glass(envMapSample4f.rgb, worldDirection4f.xyz, normalVector3f) * (1.0 - sqrt(dst2) * 0.25);
+		envMapSample4f.a += min(lightmapCoords2f.g * 2.0, 1.0) * (1.0 - envMapSample4f.a);
+		if(envMapSample4f.a == 1.0) {
+			lightColor3f += eaglercraftIBL_Specular_Glass(envMapSample4f.rgb * envMapSample4f.a, worldDirection4f.xyz, normalVector3f) * (1.0 - sqrt(dst2) * 0.2);
 		}
 		break;
 	}
diff --git a/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh b/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh
index 41f05ad7..ce15ec35 100644
--- a/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh
+++ b/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh
@@ -19,7 +19,7 @@
 precision highp sampler2DShadow;
 
 in vec4 v_position4f;
-in vec2 v_positionClip2f;
+in vec3 v_positionClip3f;
 
 #ifdef COMPILE_TEXTURE_ATTRIB
 in vec2 v_texture2f;
@@ -434,7 +434,7 @@ void main() {
 		fogBlend4f.rgb *= textureLod(u_irradianceMap, atmosSamplePos.xz, 0.0).rgb + u_fogColorAddSun4f.rgb;
 
 #ifdef COMPILE_FOG_LIGHT_SHAFTS
-		fogBlend4f.rgb *= pow(textureLod(u_lightShaftsTexture, v_positionClip2f * 0.5 + 0.5, 0.0).r * 0.9 + 0.1, 2.25);
+		fogBlend4f.rgb *= pow(textureLod(u_lightShaftsTexture, (v_positionClip3f.xy / v_positionClip3f.z) * 0.5 + 0.5, 0.0).r * 0.9 + 0.1, 2.25);
 		fogBlend4f.a = fogBlend4f.a * 0.85 + 0.2;
 #endif
 		break;
diff --git a/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.vsh b/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.vsh
index b4e04e1f..dfe5e4dd 100644
--- a/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.vsh
+++ b/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.vsh
@@ -21,7 +21,7 @@ in vec3 a_position3f;
 out vec4 v_position4f;
 
 #ifdef COMPILE_FOG_LIGHT_SHAFTS
-out vec2 v_positionClip2f;
+out vec3 v_positionClip3f;
 #endif
 
 #ifdef COMPILE_TEXTURE_ATTRIB
@@ -77,6 +77,6 @@ void main() {
 	gl_Position = u_projectionMat4f * v_position4f;
 
 #ifdef COMPILE_FOG_LIGHT_SHAFTS
-	v_positionClip2f = gl_Position.xy / gl_Position.w;
+	v_positionClip3f = gl_Position.xyw;
 #endif
 }
diff --git a/sources/resources/assets/eagler/glsl/deferred/shader_pack_info.json b/sources/resources/assets/eagler/glsl/deferred/shader_pack_info.json
index 03ad299d..a160fbd8 100644
--- a/sources/resources/assets/eagler/glsl/deferred/shader_pack_info.json
+++ b/sources/resources/assets/eagler/glsl/deferred/shader_pack_info.json
@@ -1,7 +1,7 @@
 {
 	"name": "§eHigh Performance PBR",
 	"desc": "Pack made from scratch specifically for this client, designed to give what I call the best balance between quality and performance possible in a browser but obviously that's just my opinion",
-	"vers": "1.3.0",
+	"vers": "1.3.1",
 	"author": "lax1dude",
 	"api_vers": 1,
 	"features": [
diff --git a/sources/resources/assets/eagler/glsl/texture_mix.fsh b/sources/resources/assets/eagler/glsl/texture_mix.fsh
index 26cb13fb..7ba24c18 100644
--- a/sources/resources/assets/eagler/glsl/texture_mix.fsh
+++ b/sources/resources/assets/eagler/glsl/texture_mix.fsh
@@ -1,7 +1,7 @@
 #line 2
 
 /*
- * Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
+ * Copyright (c) 2022-2025 lax1dude. All Rights Reserved.
  * 
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -16,7 +16,7 @@
  * 
  */
 
-EAGLER_IN(vec2, v_position2f)
+EAGLER_IN(vec2, v_texCoords2f)
 
 EAGLER_FRAG_OUT()
 
@@ -24,10 +24,8 @@ uniform sampler2D u_inputTexture;
 uniform float u_textureLod1f;
 uniform vec4 u_blendFactor4f;
 uniform vec4 u_blendBias4f;
-uniform mat3 u_matrixTransform;
 
 void main() {
-	vec3 coords = u_matrixTransform * vec3(v_position2f, 1.0);
-	vec4 color4f = EAGLER_TEXTURE_2D_LOD(u_inputTexture, coords.xy, u_textureLod1f);
+	vec4 color4f = EAGLER_TEXTURE_2D_LOD(u_inputTexture, v_texCoords2f, u_textureLod1f);
 	EAGLER_FRAG_COLOR = color4f * u_blendFactor4f + u_blendBias4f;
 }
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0.png
new file mode 100644
index 00000000..acadb01a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/0.png
new file mode 100644
index 00000000..482314ca
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/1.png
new file mode 100644
index 00000000..e2a575c3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/10.png
new file mode 100644
index 00000000..aa7bea62
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/11.png
new file mode 100644
index 00000000..74f57a33
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/16.png
new file mode 100644
index 00000000..d8bf7d14
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/17.png
new file mode 100644
index 00000000..0f82cfcb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/18.png
new file mode 100644
index 00000000..19290f49
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/19.png
new file mode 100644
index 00000000..ed7f6239
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/2.png
new file mode 100644
index 00000000..fb205fed
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/20.png
new file mode 100644
index 00000000..4ddd1849
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/21.png
new file mode 100644
index 00000000..c0333211
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/22.png
new file mode 100644
index 00000000..551d8634
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/23.png
new file mode 100644
index 00000000..b51bed47
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/24.png
new file mode 100644
index 00000000..52ee2065
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/25.png
new file mode 100644
index 00000000..cc5fd49c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/26.png
new file mode 100644
index 00000000..443b5b9f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/27.png
new file mode 100644
index 00000000..b7e1e8b7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/3.png
new file mode 100644
index 00000000..ab110247
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/32.png
new file mode 100644
index 00000000..8a89dbb9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/33.png
new file mode 100644
index 00000000..f426e722
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/34.png
new file mode 100644
index 00000000..8d51784a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/35.png
new file mode 100644
index 00000000..201decc9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/36.png
new file mode 100644
index 00000000..97341db4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/37.png
new file mode 100644
index 00000000..0c091e3f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/38.png
new file mode 100644
index 00000000..19589ae1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/39.png
new file mode 100644
index 00000000..3f24f73b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/4.png
new file mode 100644
index 00000000..89313f30
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/40.png
new file mode 100644
index 00000000..684b05b3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/41.png
new file mode 100644
index 00000000..68935ecf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/42.png
new file mode 100644
index 00000000..02146e6b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/43.png
new file mode 100644
index 00000000..eb033a09
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/48.png
new file mode 100644
index 00000000..b9f9e150
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/49.png
new file mode 100644
index 00000000..d8d43f32
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/5.png
new file mode 100644
index 00000000..dc8a5a23
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/50.png
new file mode 100644
index 00000000..34dfd51c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/51.png
new file mode 100644
index 00000000..adce7309
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/52.png
new file mode 100644
index 00000000..b2642eeb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/53.png
new file mode 100644
index 00000000..b986b359
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/54.png
new file mode 100644
index 00000000..51d3ace5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/55.png
new file mode 100644
index 00000000..44c38f8b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/56.png
new file mode 100644
index 00000000..8323d936
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/57.png
new file mode 100644
index 00000000..5a8a1050
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/58.png
new file mode 100644
index 00000000..8581ae84
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/6.png
new file mode 100644
index 00000000..80d0dbaa
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/7.png
new file mode 100644
index 00000000..128f2ce0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/8.png
new file mode 100644
index 00000000..05db2b11
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/9.png
new file mode 100644
index 00000000..9f12d4f9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/glass_pane_white.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/glass_pane_white.properties
new file mode 100644
index 00000000..bd738f4c
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/glass_pane_white.properties
@@ -0,0 +1,7 @@
+# Stained glass white
+matchBlocks=160
+metadata=0
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/glass_white.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/glass_white.properties
new file mode 100644
index 00000000..d762a6d2
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/0_glass_white/glass_white.properties
@@ -0,0 +1,6 @@
+# Stained glass white
+matchBlocks=95
+metadata=0
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1.png
new file mode 100644
index 00000000..291d775b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10.png
new file mode 100644
index 00000000..602786bd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/0.png
new file mode 100644
index 00000000..fcae3d26
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/1.png
new file mode 100644
index 00000000..c6388c76
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/10.png
new file mode 100644
index 00000000..025cc5d3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/11.png
new file mode 100644
index 00000000..63222ea7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/16.png
new file mode 100644
index 00000000..71a93f54
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/17.png
new file mode 100644
index 00000000..453b8682
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/18.png
new file mode 100644
index 00000000..c88f7b76
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/19.png
new file mode 100644
index 00000000..41de6632
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/2.png
new file mode 100644
index 00000000..3f58b21e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/20.png
new file mode 100644
index 00000000..52c0adaf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/21.png
new file mode 100644
index 00000000..623d894a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/22.png
new file mode 100644
index 00000000..2b1cbaab
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/23.png
new file mode 100644
index 00000000..9cffa1b6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/24.png
new file mode 100644
index 00000000..bc6aa717
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/25.png
new file mode 100644
index 00000000..eaef4e79
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/26.png
new file mode 100644
index 00000000..f4633b23
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/27.png
new file mode 100644
index 00000000..85f4fea2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/3.png
new file mode 100644
index 00000000..6a12c184
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/32.png
new file mode 100644
index 00000000..6161cf29
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/33.png
new file mode 100644
index 00000000..e17b7b38
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/34.png
new file mode 100644
index 00000000..99d1a53b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/35.png
new file mode 100644
index 00000000..3543bc41
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/36.png
new file mode 100644
index 00000000..35986d90
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/37.png
new file mode 100644
index 00000000..f328c502
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/38.png
new file mode 100644
index 00000000..219ab34a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/39.png
new file mode 100644
index 00000000..5b7580a0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/4.png
new file mode 100644
index 00000000..4e202ca7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/40.png
new file mode 100644
index 00000000..811321be
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/41.png
new file mode 100644
index 00000000..6f0e33f9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/42.png
new file mode 100644
index 00000000..1f6854a0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/43.png
new file mode 100644
index 00000000..f0a1fcc4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/48.png
new file mode 100644
index 00000000..95611680
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/49.png
new file mode 100644
index 00000000..a32e9aef
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/5.png
new file mode 100644
index 00000000..35ae1216
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/50.png
new file mode 100644
index 00000000..532325cf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/51.png
new file mode 100644
index 00000000..f900cfa7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/52.png
new file mode 100644
index 00000000..b0de565c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/53.png
new file mode 100644
index 00000000..3764a558
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/54.png
new file mode 100644
index 00000000..5ab97a6d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/55.png
new file mode 100644
index 00000000..ba3bc7ca
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/56.png
new file mode 100644
index 00000000..55007391
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/57.png
new file mode 100644
index 00000000..ff822911
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/58.png
new file mode 100644
index 00000000..e8389d70
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/6.png
new file mode 100644
index 00000000..b81516be
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/7.png
new file mode 100644
index 00000000..0efca446
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/8.png
new file mode 100644
index 00000000..fa3ab6b7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/9.png
new file mode 100644
index 00000000..b2d30509
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/glass_pane_purple.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/glass_pane_purple.properties
new file mode 100644
index 00000000..59d80f53
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/glass_pane_purple.properties
@@ -0,0 +1,7 @@
+# Stained glass purple
+matchBlocks=160
+metadata=10
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/glass_purple.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/glass_purple.properties
new file mode 100644
index 00000000..27d4480b
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/10_glass_purple/glass_purple.properties
@@ -0,0 +1,6 @@
+# Stained glass purple
+matchBlocks=95
+metadata=10
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11.png
new file mode 100644
index 00000000..a1798819
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/0.png
new file mode 100644
index 00000000..38885de7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/1.png
new file mode 100644
index 00000000..78b8685e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/10.png
new file mode 100644
index 00000000..f111abb0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/11.png
new file mode 100644
index 00000000..16827410
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/16.png
new file mode 100644
index 00000000..b69e09ea
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/17.png
new file mode 100644
index 00000000..696d771c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/18.png
new file mode 100644
index 00000000..39676be5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/19.png
new file mode 100644
index 00000000..dfa2b624
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/2.png
new file mode 100644
index 00000000..85232a51
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/20.png
new file mode 100644
index 00000000..4ffc019f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/21.png
new file mode 100644
index 00000000..58eb303e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/22.png
new file mode 100644
index 00000000..5f88c043
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/23.png
new file mode 100644
index 00000000..c52be726
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/24.png
new file mode 100644
index 00000000..c60f717e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/25.png
new file mode 100644
index 00000000..1f2b02c3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/26.png
new file mode 100644
index 00000000..e67b9151
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/27.png
new file mode 100644
index 00000000..e496c97f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/3.png
new file mode 100644
index 00000000..274c3d46
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/32.png
new file mode 100644
index 00000000..28aef713
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/33.png
new file mode 100644
index 00000000..aa469a29
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/34.png
new file mode 100644
index 00000000..b9e05b29
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/35.png
new file mode 100644
index 00000000..2e816754
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/36.png
new file mode 100644
index 00000000..95efd6af
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/37.png
new file mode 100644
index 00000000..8052ce7e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/38.png
new file mode 100644
index 00000000..b0226391
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/39.png
new file mode 100644
index 00000000..9a6bf545
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/4.png
new file mode 100644
index 00000000..c0a62e17
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/40.png
new file mode 100644
index 00000000..ad63f29b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/41.png
new file mode 100644
index 00000000..efb34d82
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/42.png
new file mode 100644
index 00000000..d6a19d5f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/43.png
new file mode 100644
index 00000000..a6cf4c95
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/48.png
new file mode 100644
index 00000000..3fcc4b5e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/49.png
new file mode 100644
index 00000000..8f7ee1dd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/5.png
new file mode 100644
index 00000000..38820075
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/50.png
new file mode 100644
index 00000000..6383a5ee
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/51.png
new file mode 100644
index 00000000..5bbff6ed
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/52.png
new file mode 100644
index 00000000..cc9ba7ec
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/53.png
new file mode 100644
index 00000000..77525994
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/54.png
new file mode 100644
index 00000000..fe348011
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/55.png
new file mode 100644
index 00000000..cb2fce7a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/56.png
new file mode 100644
index 00000000..7e6c682e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/57.png
new file mode 100644
index 00000000..3d261ad2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/58.png
new file mode 100644
index 00000000..17de9bcf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/6.png
new file mode 100644
index 00000000..66f6fc8a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/7.png
new file mode 100644
index 00000000..70eaea59
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/8.png
new file mode 100644
index 00000000..4c13eea1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/9.png
new file mode 100644
index 00000000..16a4c834
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/glass_blue.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/glass_blue.properties
new file mode 100644
index 00000000..b9441ea5
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/glass_blue.properties
@@ -0,0 +1,6 @@
+# Stained glass blue
+matchBlocks=95
+metadata=11
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/glass_pane_blue.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/glass_pane_blue.properties
new file mode 100644
index 00000000..61c68c24
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/11_glass_blue/glass_pane_blue.properties
@@ -0,0 +1,7 @@
+# Stained glass blue
+matchBlocks=160
+metadata=11
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12.png
new file mode 100644
index 00000000..93a10038
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/0.png
new file mode 100644
index 00000000..259b61c6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/1.png
new file mode 100644
index 00000000..2268ce66
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/10.png
new file mode 100644
index 00000000..7ac2776f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/11.png
new file mode 100644
index 00000000..6dcaeace
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/16.png
new file mode 100644
index 00000000..d1d37415
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/17.png
new file mode 100644
index 00000000..b94809b7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/18.png
new file mode 100644
index 00000000..5459fac8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/19.png
new file mode 100644
index 00000000..afb4bc82
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/2.png
new file mode 100644
index 00000000..c07848c3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/20.png
new file mode 100644
index 00000000..2f381e27
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/21.png
new file mode 100644
index 00000000..abaa5cdd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/22.png
new file mode 100644
index 00000000..666c8e9a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/23.png
new file mode 100644
index 00000000..2447f128
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/24.png
new file mode 100644
index 00000000..b017b2ea
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/25.png
new file mode 100644
index 00000000..acaaf58d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/26.png
new file mode 100644
index 00000000..70ec7727
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/27.png
new file mode 100644
index 00000000..c5e69d1c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/3.png
new file mode 100644
index 00000000..b7f2e56a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/32.png
new file mode 100644
index 00000000..e175b989
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/33.png
new file mode 100644
index 00000000..799c6a10
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/34.png
new file mode 100644
index 00000000..ff9667fc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/35.png
new file mode 100644
index 00000000..f8909407
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/36.png
new file mode 100644
index 00000000..26d3a7ad
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/37.png
new file mode 100644
index 00000000..5e2cd71c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/38.png
new file mode 100644
index 00000000..ee987d8f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/39.png
new file mode 100644
index 00000000..940b86cc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/4.png
new file mode 100644
index 00000000..167feabc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/40.png
new file mode 100644
index 00000000..46108509
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/41.png
new file mode 100644
index 00000000..e87f9cb1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/42.png
new file mode 100644
index 00000000..a0073198
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/43.png
new file mode 100644
index 00000000..62a6c950
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/48.png
new file mode 100644
index 00000000..3003b361
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/49.png
new file mode 100644
index 00000000..0c87b12b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/5.png
new file mode 100644
index 00000000..363d09b8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/50.png
new file mode 100644
index 00000000..bff4e7a7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/51.png
new file mode 100644
index 00000000..85f2aba6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/52.png
new file mode 100644
index 00000000..1422a56e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/53.png
new file mode 100644
index 00000000..4a084131
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/54.png
new file mode 100644
index 00000000..43bcdb8e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/55.png
new file mode 100644
index 00000000..21487fe0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/56.png
new file mode 100644
index 00000000..aaece811
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/57.png
new file mode 100644
index 00000000..d4ba78de
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/58.png
new file mode 100644
index 00000000..746ae6a3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/6.png
new file mode 100644
index 00000000..ac145b84
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/7.png
new file mode 100644
index 00000000..b9ad6b65
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/8.png
new file mode 100644
index 00000000..f9febbcf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/9.png
new file mode 100644
index 00000000..1a002863
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/glass_brown.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/glass_brown.properties
new file mode 100644
index 00000000..0b33e3cb
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/glass_brown.properties
@@ -0,0 +1,6 @@
+# Stained glass brown
+matchBlocks=95
+metadata=12
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/glass_pane_brown.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/glass_pane_brown.properties
new file mode 100644
index 00000000..8da13ce6
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/12_glass_brown/glass_pane_brown.properties
@@ -0,0 +1,7 @@
+# Stained glass brown
+matchBlocks=160
+metadata=12
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13.png
new file mode 100644
index 00000000..d7f8277f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/0.png
new file mode 100644
index 00000000..7c1f4e60
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/1.png
new file mode 100644
index 00000000..2040368e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/10.png
new file mode 100644
index 00000000..3c9fdef0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/11.png
new file mode 100644
index 00000000..4bfcc6d9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/16.png
new file mode 100644
index 00000000..edc28eb8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/17.png
new file mode 100644
index 00000000..3b29a9e7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/18.png
new file mode 100644
index 00000000..f9513cb9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/19.png
new file mode 100644
index 00000000..83d1cd4a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/2.png
new file mode 100644
index 00000000..62f1d66d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/20.png
new file mode 100644
index 00000000..f5d223a9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/21.png
new file mode 100644
index 00000000..001eba06
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/22.png
new file mode 100644
index 00000000..b1e82746
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/23.png
new file mode 100644
index 00000000..a4d6cc31
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/24.png
new file mode 100644
index 00000000..d4708573
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/25.png
new file mode 100644
index 00000000..1c52d05c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/26.png
new file mode 100644
index 00000000..e1a22b64
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/27.png
new file mode 100644
index 00000000..92eddbc3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/3.png
new file mode 100644
index 00000000..9efad318
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/32.png
new file mode 100644
index 00000000..2d4ab467
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/33.png
new file mode 100644
index 00000000..8d282896
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/34.png
new file mode 100644
index 00000000..b2aa6f9f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/35.png
new file mode 100644
index 00000000..120a9bd7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/36.png
new file mode 100644
index 00000000..19ca5f5a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/37.png
new file mode 100644
index 00000000..8a8c17ee
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/38.png
new file mode 100644
index 00000000..8e6f98ab
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/39.png
new file mode 100644
index 00000000..cb616923
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/4.png
new file mode 100644
index 00000000..f3003ac3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/40.png
new file mode 100644
index 00000000..ccb2dc67
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/41.png
new file mode 100644
index 00000000..6373bf13
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/42.png
new file mode 100644
index 00000000..ccebff18
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/43.png
new file mode 100644
index 00000000..ae5e9481
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/48.png
new file mode 100644
index 00000000..a818c9fe
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/49.png
new file mode 100644
index 00000000..7705c143
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/5.png
new file mode 100644
index 00000000..c6375173
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/50.png
new file mode 100644
index 00000000..2c3c4b8c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/51.png
new file mode 100644
index 00000000..ec24732f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/52.png
new file mode 100644
index 00000000..dd1ae3b8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/53.png
new file mode 100644
index 00000000..50f722c5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/54.png
new file mode 100644
index 00000000..8cc645ab
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/55.png
new file mode 100644
index 00000000..3a8aa9bc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/56.png
new file mode 100644
index 00000000..8298ffd9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/57.png
new file mode 100644
index 00000000..f81edbca
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/58.png
new file mode 100644
index 00000000..a3ff7e6f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/6.png
new file mode 100644
index 00000000..89bf8899
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/7.png
new file mode 100644
index 00000000..7d264a9c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/8.png
new file mode 100644
index 00000000..9a9725fe
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/9.png
new file mode 100644
index 00000000..9645de2f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/glass_green.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/glass_green.properties
new file mode 100644
index 00000000..0e2e5b34
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/glass_green.properties
@@ -0,0 +1,6 @@
+# Stained glass green
+matchBlocks=95
+metadata=13
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/glass_pane_green.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/glass_pane_green.properties
new file mode 100644
index 00000000..4082f196
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/13_glass_green/glass_pane_green.properties
@@ -0,0 +1,7 @@
+# Stained glass green
+matchBlocks=160
+metadata=13
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14.png
new file mode 100644
index 00000000..ea6408b8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/0.png
new file mode 100644
index 00000000..db4c5eb7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/1.png
new file mode 100644
index 00000000..3e8c7692
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/10.png
new file mode 100644
index 00000000..247b8156
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/11.png
new file mode 100644
index 00000000..e4497bf5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/16.png
new file mode 100644
index 00000000..3d567bee
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/17.png
new file mode 100644
index 00000000..b593cb89
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/18.png
new file mode 100644
index 00000000..42924abc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/19.png
new file mode 100644
index 00000000..db7d4e9c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/2.png
new file mode 100644
index 00000000..72f461b0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/20.png
new file mode 100644
index 00000000..619b2e7d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/21.png
new file mode 100644
index 00000000..363c81ef
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/22.png
new file mode 100644
index 00000000..e60f6273
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/23.png
new file mode 100644
index 00000000..f439b47c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/24.png
new file mode 100644
index 00000000..b62e65b6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/25.png
new file mode 100644
index 00000000..ae1c143a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/26.png
new file mode 100644
index 00000000..cc952b33
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/27.png
new file mode 100644
index 00000000..32ad0f25
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/3.png
new file mode 100644
index 00000000..69b8bb31
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/32.png
new file mode 100644
index 00000000..6f31642b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/33.png
new file mode 100644
index 00000000..3bae303e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/34.png
new file mode 100644
index 00000000..074a5276
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/35.png
new file mode 100644
index 00000000..336f054c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/36.png
new file mode 100644
index 00000000..6aea7776
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/37.png
new file mode 100644
index 00000000..382bf646
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/38.png
new file mode 100644
index 00000000..88faee4e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/39.png
new file mode 100644
index 00000000..c452b15d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/4.png
new file mode 100644
index 00000000..de22d1e9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/40.png
new file mode 100644
index 00000000..da6f8768
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/41.png
new file mode 100644
index 00000000..a25b3aff
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/42.png
new file mode 100644
index 00000000..c437e2cc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/43.png
new file mode 100644
index 00000000..dbff65db
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/48.png
new file mode 100644
index 00000000..c2aa088e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/49.png
new file mode 100644
index 00000000..64f2e3c6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/5.png
new file mode 100644
index 00000000..64c2d642
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/50.png
new file mode 100644
index 00000000..3903aab5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/51.png
new file mode 100644
index 00000000..5491189a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/52.png
new file mode 100644
index 00000000..61a217de
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/53.png
new file mode 100644
index 00000000..de9db5e5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/54.png
new file mode 100644
index 00000000..3c3f5bb0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/55.png
new file mode 100644
index 00000000..b221b3d8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/56.png
new file mode 100644
index 00000000..b4799e59
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/57.png
new file mode 100644
index 00000000..c0b53702
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/58.png
new file mode 100644
index 00000000..fec4f8fe
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/6.png
new file mode 100644
index 00000000..3d59656f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/7.png
new file mode 100644
index 00000000..c83cac50
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/8.png
new file mode 100644
index 00000000..59e1bc34
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/9.png
new file mode 100644
index 00000000..eecb4a77
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/glass_pane_red.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/glass_pane_red.properties
new file mode 100644
index 00000000..b488d99f
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/glass_pane_red.properties
@@ -0,0 +1,7 @@
+# Stained glass red
+matchBlocks=160
+metadata=14
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/glass_red.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/glass_red.properties
new file mode 100644
index 00000000..cf43547f
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/14_glass_red/glass_red.properties
@@ -0,0 +1,6 @@
+# Stained glass red
+matchBlocks=95
+metadata=14
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15.png
new file mode 100644
index 00000000..4c87f0fb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/0.png
new file mode 100644
index 00000000..06f34279
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/1.png
new file mode 100644
index 00000000..7e63ee76
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/10.png
new file mode 100644
index 00000000..9142b6f2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/11.png
new file mode 100644
index 00000000..f031f1c9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/16.png
new file mode 100644
index 00000000..273dc8c3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/17.png
new file mode 100644
index 00000000..0c367992
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/18.png
new file mode 100644
index 00000000..6275d954
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/19.png
new file mode 100644
index 00000000..8ba895b6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/2.png
new file mode 100644
index 00000000..32c68724
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/20.png
new file mode 100644
index 00000000..bc2543ee
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/21.png
new file mode 100644
index 00000000..2f7fe8ef
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/22.png
new file mode 100644
index 00000000..0d5f9f2b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/23.png
new file mode 100644
index 00000000..3aa27e5e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/24.png
new file mode 100644
index 00000000..f0ded67f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/25.png
new file mode 100644
index 00000000..dbb6121f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/26.png
new file mode 100644
index 00000000..69bfbc03
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/27.png
new file mode 100644
index 00000000..07f46ac8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/3.png
new file mode 100644
index 00000000..011ca1e2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/32.png
new file mode 100644
index 00000000..5512cbb4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/33.png
new file mode 100644
index 00000000..239fd78a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/34.png
new file mode 100644
index 00000000..defc0689
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/35.png
new file mode 100644
index 00000000..73dd6df9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/36.png
new file mode 100644
index 00000000..8185b75e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/37.png
new file mode 100644
index 00000000..f55e9601
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/38.png
new file mode 100644
index 00000000..6a125742
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/39.png
new file mode 100644
index 00000000..f7dadbbf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/4.png
new file mode 100644
index 00000000..21e9e9ef
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/40.png
new file mode 100644
index 00000000..a0f9b0ae
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/41.png
new file mode 100644
index 00000000..a191e236
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/42.png
new file mode 100644
index 00000000..d12d6b81
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/43.png
new file mode 100644
index 00000000..8d55d970
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/48.png
new file mode 100644
index 00000000..bf8fe4ac
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/49.png
new file mode 100644
index 00000000..833a3e22
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/5.png
new file mode 100644
index 00000000..0b09c259
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/50.png
new file mode 100644
index 00000000..5814d063
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/51.png
new file mode 100644
index 00000000..c2decd50
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/52.png
new file mode 100644
index 00000000..b87f6e4b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/53.png
new file mode 100644
index 00000000..99fd221b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/54.png
new file mode 100644
index 00000000..7b5e6306
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/55.png
new file mode 100644
index 00000000..10be40b5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/56.png
new file mode 100644
index 00000000..96c8041e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/57.png
new file mode 100644
index 00000000..5d2638a4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/58.png
new file mode 100644
index 00000000..9360f4a4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/6.png
new file mode 100644
index 00000000..143555f0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/7.png
new file mode 100644
index 00000000..443fa690
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/8.png
new file mode 100644
index 00000000..6888680c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/9.png
new file mode 100644
index 00000000..41b217d1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/glass_black.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/glass_black.properties
new file mode 100644
index 00000000..4566909d
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/glass_black.properties
@@ -0,0 +1,6 @@
+# Stained glass black
+matchBlocks=95
+metadata=15
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/glass_pane_black.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/glass_pane_black.properties
new file mode 100644
index 00000000..97e092d6
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/15_glass_black/glass_pane_black.properties
@@ -0,0 +1,7 @@
+# Stained glass black
+matchBlocks=160
+metadata=15
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/16.png
new file mode 100644
index 00000000..316522f4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/17.png
new file mode 100644
index 00000000..b3c31653
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/18.png
new file mode 100644
index 00000000..70de401e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/19.png
new file mode 100644
index 00000000..c8704a1a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/0.png
new file mode 100644
index 00000000..3a29e31b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/1.png
new file mode 100644
index 00000000..ac20892d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/10.png
new file mode 100644
index 00000000..51079039
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/11.png
new file mode 100644
index 00000000..76a4ecb1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/16.png
new file mode 100644
index 00000000..34b4e01d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/17.png
new file mode 100644
index 00000000..d5ba7a0e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/18.png
new file mode 100644
index 00000000..31ea8b59
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/19.png
new file mode 100644
index 00000000..3fa6a203
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/2.png
new file mode 100644
index 00000000..da2c361a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/20.png
new file mode 100644
index 00000000..2eb2915b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/21.png
new file mode 100644
index 00000000..09652b32
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/22.png
new file mode 100644
index 00000000..92e199c1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/23.png
new file mode 100644
index 00000000..9068d194
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/24.png
new file mode 100644
index 00000000..f07d9790
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/25.png
new file mode 100644
index 00000000..520102cf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/26.png
new file mode 100644
index 00000000..be490ad2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/27.png
new file mode 100644
index 00000000..3eac57fc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/3.png
new file mode 100644
index 00000000..0cb6300b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/32.png
new file mode 100644
index 00000000..38fe9f48
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/33.png
new file mode 100644
index 00000000..ad6398d5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/34.png
new file mode 100644
index 00000000..53d76db3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/35.png
new file mode 100644
index 00000000..c7adaab1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/36.png
new file mode 100644
index 00000000..de55e7ba
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/37.png
new file mode 100644
index 00000000..1202c0d5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/38.png
new file mode 100644
index 00000000..8977b0fc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/39.png
new file mode 100644
index 00000000..c091f83a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/4.png
new file mode 100644
index 00000000..595ce651
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/40.png
new file mode 100644
index 00000000..f51e5894
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/41.png
new file mode 100644
index 00000000..663b197d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/42.png
new file mode 100644
index 00000000..f6a65d12
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/43.png
new file mode 100644
index 00000000..4b6a0d5d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/48.png
new file mode 100644
index 00000000..4e6c92a4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/49.png
new file mode 100644
index 00000000..1a5751d5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/5.png
new file mode 100644
index 00000000..c9353d4b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/50.png
new file mode 100644
index 00000000..b73c34f3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/51.png
new file mode 100644
index 00000000..ab60c8ea
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/52.png
new file mode 100644
index 00000000..282c59d7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/53.png
new file mode 100644
index 00000000..3df68a28
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/54.png
new file mode 100644
index 00000000..36a6ae1e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/55.png
new file mode 100644
index 00000000..73ff42d7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/56.png
new file mode 100644
index 00000000..4bc7b529
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/57.png
new file mode 100644
index 00000000..3df9c2a6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/58.png
new file mode 100644
index 00000000..067054d3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/6.png
new file mode 100644
index 00000000..f310397f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/7.png
new file mode 100644
index 00000000..6d06d7b8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/8.png
new file mode 100644
index 00000000..ac561ba7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/9.png
new file mode 100644
index 00000000..42034d27
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/glass_orange.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/glass_orange.properties
new file mode 100644
index 00000000..41553be1
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/glass_orange.properties
@@ -0,0 +1,6 @@
+# Stained glass orange
+matchBlocks=95
+metadata=1
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/glass_pane_orange.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/glass_pane_orange.properties
new file mode 100644
index 00000000..84b6f02b
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/1_glass_orange/glass_pane_orange.properties
@@ -0,0 +1,7 @@
+# Stained glass orange
+matchBlocks=160
+metadata=1
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2.png
new file mode 100644
index 00000000..dc088c6e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/20.png
new file mode 100644
index 00000000..8b54006c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/21.png
new file mode 100644
index 00000000..193abdc9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/22.png
new file mode 100644
index 00000000..13940d42
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/23.png
new file mode 100644
index 00000000..a64d95f8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/24.png
new file mode 100644
index 00000000..0291a2eb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/25.png
new file mode 100644
index 00000000..ea459b60
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/26.png
new file mode 100644
index 00000000..683cfe1b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/27.png
new file mode 100644
index 00000000..45a0af6a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/0.png
new file mode 100644
index 00000000..5cd99450
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/1.png
new file mode 100644
index 00000000..73799d2a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/10.png
new file mode 100644
index 00000000..f307b826
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/11.png
new file mode 100644
index 00000000..612745db
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/16.png
new file mode 100644
index 00000000..5eaccfcd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/17.png
new file mode 100644
index 00000000..1e206b4c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/18.png
new file mode 100644
index 00000000..30e55def
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/19.png
new file mode 100644
index 00000000..7fa751ca
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/2.png
new file mode 100644
index 00000000..dd377961
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/20.png
new file mode 100644
index 00000000..145d9a67
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/21.png
new file mode 100644
index 00000000..a389d826
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/22.png
new file mode 100644
index 00000000..e1379a81
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/23.png
new file mode 100644
index 00000000..b9ba5b91
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/24.png
new file mode 100644
index 00000000..c80687a7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/25.png
new file mode 100644
index 00000000..39f403c5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/26.png
new file mode 100644
index 00000000..638b3da0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/27.png
new file mode 100644
index 00000000..e88b2e61
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/3.png
new file mode 100644
index 00000000..12f1aa95
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/32.png
new file mode 100644
index 00000000..e4621f38
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/33.png
new file mode 100644
index 00000000..a64a1703
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/34.png
new file mode 100644
index 00000000..e642e141
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/35.png
new file mode 100644
index 00000000..7ee2dba2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/36.png
new file mode 100644
index 00000000..c7e553e8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/37.png
new file mode 100644
index 00000000..5d91ebf0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/38.png
new file mode 100644
index 00000000..3a167e87
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/39.png
new file mode 100644
index 00000000..19fe7f1c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/4.png
new file mode 100644
index 00000000..b1ff09ce
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/40.png
new file mode 100644
index 00000000..999f9c78
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/41.png
new file mode 100644
index 00000000..27a39e1c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/42.png
new file mode 100644
index 00000000..495859f3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/43.png
new file mode 100644
index 00000000..0de323b5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/48.png
new file mode 100644
index 00000000..c0cda909
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/49.png
new file mode 100644
index 00000000..0acb361c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/5.png
new file mode 100644
index 00000000..5f09b94c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/50.png
new file mode 100644
index 00000000..8e138e52
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/51.png
new file mode 100644
index 00000000..1b0d1ba9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/52.png
new file mode 100644
index 00000000..3bc00be7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/53.png
new file mode 100644
index 00000000..f81704ec
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/54.png
new file mode 100644
index 00000000..5520a916
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/55.png
new file mode 100644
index 00000000..d83d0da3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/56.png
new file mode 100644
index 00000000..3b2f2d6e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/57.png
new file mode 100644
index 00000000..492142e7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/58.png
new file mode 100644
index 00000000..30fc4c9f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/6.png
new file mode 100644
index 00000000..bce21a58
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/7.png
new file mode 100644
index 00000000..659eb3af
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/8.png
new file mode 100644
index 00000000..0763cb66
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/9.png
new file mode 100644
index 00000000..6628eb88
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/glass_magenta.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/glass_magenta.properties
new file mode 100644
index 00000000..ddf78f79
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/glass_magenta.properties
@@ -0,0 +1,6 @@
+# Stained glass magenta
+matchBlocks=95
+metadata=2
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/glass_pane_magenta.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/glass_pane_magenta.properties
new file mode 100644
index 00000000..3647ad59
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/2_glass_magenta/glass_pane_magenta.properties
@@ -0,0 +1,7 @@
+# Stained glass magenta
+matchBlocks=160
+metadata=2
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3.png
new file mode 100644
index 00000000..6611ddcd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/32.png
new file mode 100644
index 00000000..6f2391c2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/33.png
new file mode 100644
index 00000000..95721237
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/34.png
new file mode 100644
index 00000000..a620f448
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/35.png
new file mode 100644
index 00000000..af69add9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/36.png
new file mode 100644
index 00000000..5c7effda
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/37.png
new file mode 100644
index 00000000..663c27de
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/38.png
new file mode 100644
index 00000000..21b4b1b5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/39.png
new file mode 100644
index 00000000..8ded4a68
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/0.png
new file mode 100644
index 00000000..b62703d4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/1.png
new file mode 100644
index 00000000..b54d3e07
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/10.png
new file mode 100644
index 00000000..61a2de60
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/11.png
new file mode 100644
index 00000000..0d65d441
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/16.png
new file mode 100644
index 00000000..3e2154f7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/17.png
new file mode 100644
index 00000000..5bfc9b92
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/18.png
new file mode 100644
index 00000000..0eaf5fc8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/19.png
new file mode 100644
index 00000000..899033b4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/2.png
new file mode 100644
index 00000000..5ff1e34c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/20.png
new file mode 100644
index 00000000..1053015a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/21.png
new file mode 100644
index 00000000..2ca7ba64
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/22.png
new file mode 100644
index 00000000..972314f7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/23.png
new file mode 100644
index 00000000..471b0a17
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/24.png
new file mode 100644
index 00000000..60fc60da
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/25.png
new file mode 100644
index 00000000..c7a6d9b6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/26.png
new file mode 100644
index 00000000..881c6591
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/27.png
new file mode 100644
index 00000000..b7dfaa3b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/3.png
new file mode 100644
index 00000000..abd672d4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/32.png
new file mode 100644
index 00000000..63a1c25a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/33.png
new file mode 100644
index 00000000..d0c743f9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/34.png
new file mode 100644
index 00000000..98555c33
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/35.png
new file mode 100644
index 00000000..f2c50a22
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/36.png
new file mode 100644
index 00000000..21d68133
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/37.png
new file mode 100644
index 00000000..cb543130
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/38.png
new file mode 100644
index 00000000..a148c54d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/39.png
new file mode 100644
index 00000000..6328228c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/4.png
new file mode 100644
index 00000000..a996a89a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/40.png
new file mode 100644
index 00000000..520b4838
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/41.png
new file mode 100644
index 00000000..6b686715
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/42.png
new file mode 100644
index 00000000..84aaf0d3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/43.png
new file mode 100644
index 00000000..585fe05d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/48.png
new file mode 100644
index 00000000..4734b0ad
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/49.png
new file mode 100644
index 00000000..1b47e77e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/5.png
new file mode 100644
index 00000000..40927d64
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/50.png
new file mode 100644
index 00000000..a806848b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/51.png
new file mode 100644
index 00000000..b521d2fa
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/52.png
new file mode 100644
index 00000000..eb9e2f62
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/53.png
new file mode 100644
index 00000000..dba86fd1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/54.png
new file mode 100644
index 00000000..58e9d743
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/55.png
new file mode 100644
index 00000000..49f8af31
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/56.png
new file mode 100644
index 00000000..4729ae03
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/57.png
new file mode 100644
index 00000000..de1a51f7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/58.png
new file mode 100644
index 00000000..d7f7c6e5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/6.png
new file mode 100644
index 00000000..3005d8c9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/7.png
new file mode 100644
index 00000000..70c377b3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/8.png
new file mode 100644
index 00000000..4eeb2f21
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/9.png
new file mode 100644
index 00000000..f6bb6abc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/glass_light_blue.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/glass_light_blue.properties
new file mode 100644
index 00000000..7f84e327
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/glass_light_blue.properties
@@ -0,0 +1,6 @@
+# Stained glass light_blue
+matchBlocks=95
+metadata=3
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/glass_pane_light_blue.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/glass_pane_light_blue.properties
new file mode 100644
index 00000000..326c9508
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/3_glass_light_blue/glass_pane_light_blue.properties
@@ -0,0 +1,7 @@
+# Stained glass light_blue
+matchBlocks=160
+metadata=3
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4.png
new file mode 100644
index 00000000..ba09a5e3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/40.png
new file mode 100644
index 00000000..1fb540bb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/41.png
new file mode 100644
index 00000000..1ae5be78
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/42.png
new file mode 100644
index 00000000..bd95188e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/43.png
new file mode 100644
index 00000000..00e20971
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/48.png
new file mode 100644
index 00000000..bdbb21c0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/49.png
new file mode 100644
index 00000000..72b55b58
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/0.png
new file mode 100644
index 00000000..8e48e120
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/1.png
new file mode 100644
index 00000000..7ee50cf6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/10.png
new file mode 100644
index 00000000..d5e92663
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/11.png
new file mode 100644
index 00000000..11561fe9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/16.png
new file mode 100644
index 00000000..e1e7e8ac
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/17.png
new file mode 100644
index 00000000..5c6b658e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/18.png
new file mode 100644
index 00000000..f0537918
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/19.png
new file mode 100644
index 00000000..9d2c4d67
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/2.png
new file mode 100644
index 00000000..d7899b56
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/20.png
new file mode 100644
index 00000000..97ab9ab2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/21.png
new file mode 100644
index 00000000..31a4fea4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/22.png
new file mode 100644
index 00000000..0e4844f4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/23.png
new file mode 100644
index 00000000..d3ba7508
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/24.png
new file mode 100644
index 00000000..5b54d6a0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/25.png
new file mode 100644
index 00000000..acd47fd0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/26.png
new file mode 100644
index 00000000..d975f899
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/27.png
new file mode 100644
index 00000000..b544aba6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/3.png
new file mode 100644
index 00000000..c6cd55e4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/32.png
new file mode 100644
index 00000000..05009544
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/33.png
new file mode 100644
index 00000000..92ec3386
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/34.png
new file mode 100644
index 00000000..499f0613
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/35.png
new file mode 100644
index 00000000..e0a0567d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/36.png
new file mode 100644
index 00000000..a3f7fce4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/37.png
new file mode 100644
index 00000000..faf281b8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/38.png
new file mode 100644
index 00000000..77aa4373
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/39.png
new file mode 100644
index 00000000..ee7d8fee
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/4.png
new file mode 100644
index 00000000..e19d2eba
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/40.png
new file mode 100644
index 00000000..fb1f1aeb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/41.png
new file mode 100644
index 00000000..7974f3ce
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/42.png
new file mode 100644
index 00000000..6d999ba1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/43.png
new file mode 100644
index 00000000..c85d599f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/48.png
new file mode 100644
index 00000000..bf1e5f52
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/49.png
new file mode 100644
index 00000000..63ffd318
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/5.png
new file mode 100644
index 00000000..bc4916a1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/50.png
new file mode 100644
index 00000000..fd296c35
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/51.png
new file mode 100644
index 00000000..5989037f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/52.png
new file mode 100644
index 00000000..849b4984
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/53.png
new file mode 100644
index 00000000..149ed0c6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/54.png
new file mode 100644
index 00000000..ff41948e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/55.png
new file mode 100644
index 00000000..25ef075d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/56.png
new file mode 100644
index 00000000..ac21fe86
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/57.png
new file mode 100644
index 00000000..82e59f99
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/58.png
new file mode 100644
index 00000000..e9ec17d8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/6.png
new file mode 100644
index 00000000..9038f0c8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/7.png
new file mode 100644
index 00000000..55bbba6e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/8.png
new file mode 100644
index 00000000..7c658260
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/9.png
new file mode 100644
index 00000000..f18633ec
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/glass_pane_yellow.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/glass_pane_yellow.properties
new file mode 100644
index 00000000..6b3655e2
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/glass_pane_yellow.properties
@@ -0,0 +1,7 @@
+# Stained glass yellow
+matchBlocks=160
+metadata=4
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/glass_yellow.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/glass_yellow.properties
new file mode 100644
index 00000000..064de857
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/4_glass_yellow/glass_yellow.properties
@@ -0,0 +1,6 @@
+# Stained glass yellow
+matchBlocks=95
+metadata=4
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5.png
new file mode 100644
index 00000000..3f475db9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/50.png
new file mode 100644
index 00000000..9de42fed
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/51.png
new file mode 100644
index 00000000..3eb78ec8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/52.png
new file mode 100644
index 00000000..c6deb568
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/53.png
new file mode 100644
index 00000000..ccb3ad47
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/54.png
new file mode 100644
index 00000000..db2b9cc6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/55.png
new file mode 100644
index 00000000..c4ba83b3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/56.png
new file mode 100644
index 00000000..896a612e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/57.png
new file mode 100644
index 00000000..40c7712d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/58.png
new file mode 100644
index 00000000..66cf887b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/0.png
new file mode 100644
index 00000000..f1d3c46a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/1.png
new file mode 100644
index 00000000..28ff5aa6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/10.png
new file mode 100644
index 00000000..52548e91
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/11.png
new file mode 100644
index 00000000..3fc29f5a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/16.png
new file mode 100644
index 00000000..bef180c6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/17.png
new file mode 100644
index 00000000..365032be
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/18.png
new file mode 100644
index 00000000..99006ad2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/19.png
new file mode 100644
index 00000000..3daa9530
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/2.png
new file mode 100644
index 00000000..2a5941b1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/20.png
new file mode 100644
index 00000000..da36e376
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/21.png
new file mode 100644
index 00000000..158596dd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/22.png
new file mode 100644
index 00000000..68383dde
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/23.png
new file mode 100644
index 00000000..2d60be09
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/24.png
new file mode 100644
index 00000000..5a41ebed
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/25.png
new file mode 100644
index 00000000..4512b611
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/26.png
new file mode 100644
index 00000000..6a34f7a3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/27.png
new file mode 100644
index 00000000..d50f7513
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/3.png
new file mode 100644
index 00000000..4ad6261f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/32.png
new file mode 100644
index 00000000..4a477d20
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/33.png
new file mode 100644
index 00000000..b3d3c4cf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/34.png
new file mode 100644
index 00000000..cdbf635a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/35.png
new file mode 100644
index 00000000..8d048016
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/36.png
new file mode 100644
index 00000000..96fe6bdd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/37.png
new file mode 100644
index 00000000..7c6557a5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/38.png
new file mode 100644
index 00000000..d0f480b3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/39.png
new file mode 100644
index 00000000..6a84d7cc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/4.png
new file mode 100644
index 00000000..045109c3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/40.png
new file mode 100644
index 00000000..11542e15
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/41.png
new file mode 100644
index 00000000..99fd3477
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/42.png
new file mode 100644
index 00000000..a4dc944d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/43.png
new file mode 100644
index 00000000..5d1eef1c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/48.png
new file mode 100644
index 00000000..cf228f54
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/49.png
new file mode 100644
index 00000000..42568656
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/5.png
new file mode 100644
index 00000000..ba16e32a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/50.png
new file mode 100644
index 00000000..3b23cb59
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/51.png
new file mode 100644
index 00000000..c29337ad
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/52.png
new file mode 100644
index 00000000..0f33f4d8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/53.png
new file mode 100644
index 00000000..b4887a32
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/54.png
new file mode 100644
index 00000000..797aaa1d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/55.png
new file mode 100644
index 00000000..9817194c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/56.png
new file mode 100644
index 00000000..8e3eba1f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/57.png
new file mode 100644
index 00000000..b7454e1b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/58.png
new file mode 100644
index 00000000..f5786455
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/6.png
new file mode 100644
index 00000000..d2e9aded
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/7.png
new file mode 100644
index 00000000..9f26f1b6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/8.png
new file mode 100644
index 00000000..6e44ca17
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/9.png
new file mode 100644
index 00000000..3a61657b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/glass_lime.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/glass_lime.properties
new file mode 100644
index 00000000..968e822b
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/glass_lime.properties
@@ -0,0 +1,6 @@
+# Stained glass lime
+matchBlocks=95
+metadata=5
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/glass_pane_lime.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/glass_pane_lime.properties
new file mode 100644
index 00000000..108acbca
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/5_glass_lime/glass_pane_lime.properties
@@ -0,0 +1,7 @@
+# Stained glass lime
+matchBlocks=160
+metadata=5
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6.png
new file mode 100644
index 00000000..ffd366db
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/66.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/66.png
new file mode 100644
index 00000000..13d68d0d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/66.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/0.png
new file mode 100644
index 00000000..42d8739d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/1.png
new file mode 100644
index 00000000..7744913f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/10.png
new file mode 100644
index 00000000..a87f362c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/11.png
new file mode 100644
index 00000000..9f572a8e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/16.png
new file mode 100644
index 00000000..9cc8ef9f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/17.png
new file mode 100644
index 00000000..947f4ac0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/18.png
new file mode 100644
index 00000000..fea9bca9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/19.png
new file mode 100644
index 00000000..fd0ee452
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/2.png
new file mode 100644
index 00000000..50ef16b0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/20.png
new file mode 100644
index 00000000..378e5efe
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/21.png
new file mode 100644
index 00000000..557d38d1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/22.png
new file mode 100644
index 00000000..a9f4eb0c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/23.png
new file mode 100644
index 00000000..6b74afcb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/24.png
new file mode 100644
index 00000000..d6bb6434
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/25.png
new file mode 100644
index 00000000..be3c1a27
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/26.png
new file mode 100644
index 00000000..7f87f517
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/27.png
new file mode 100644
index 00000000..ac497a7b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/3.png
new file mode 100644
index 00000000..078a36af
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/32.png
new file mode 100644
index 00000000..2bb9b0d0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/33.png
new file mode 100644
index 00000000..88a776dc
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/34.png
new file mode 100644
index 00000000..35c3098f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/35.png
new file mode 100644
index 00000000..0ad4c4e6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/36.png
new file mode 100644
index 00000000..1360eea1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/37.png
new file mode 100644
index 00000000..3ba09fc3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/38.png
new file mode 100644
index 00000000..734a7cfa
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/39.png
new file mode 100644
index 00000000..fd3329f0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/4.png
new file mode 100644
index 00000000..02d6d030
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/40.png
new file mode 100644
index 00000000..2c41cc1b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/41.png
new file mode 100644
index 00000000..113d0c27
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/42.png
new file mode 100644
index 00000000..c1ab1103
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/43.png
new file mode 100644
index 00000000..600cfe2d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/48.png
new file mode 100644
index 00000000..80182e50
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/49.png
new file mode 100644
index 00000000..f655fb95
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/5.png
new file mode 100644
index 00000000..b94667c7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/50.png
new file mode 100644
index 00000000..23141561
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/51.png
new file mode 100644
index 00000000..bfda4f28
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/52.png
new file mode 100644
index 00000000..3e167679
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/53.png
new file mode 100644
index 00000000..37c84eca
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/54.png
new file mode 100644
index 00000000..c971dfcb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/55.png
new file mode 100644
index 00000000..c0f4df80
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/56.png
new file mode 100644
index 00000000..16374c39
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/57.png
new file mode 100644
index 00000000..68155bad
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/58.png
new file mode 100644
index 00000000..016889e3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/6.png
new file mode 100644
index 00000000..5b0cb15e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/7.png
new file mode 100644
index 00000000..4e3885e3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/8.png
new file mode 100644
index 00000000..a2c6f48a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/9.png
new file mode 100644
index 00000000..0051d1d8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/glass_pane_pink.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/glass_pane_pink.properties
new file mode 100644
index 00000000..28dff1a9
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/glass_pane_pink.properties
@@ -0,0 +1,7 @@
+# Stained glass pink
+matchBlocks=160
+metadata=6
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/glass_pink.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/glass_pink.properties
new file mode 100644
index 00000000..1bbca386
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/6_glass_pink/glass_pink.properties
@@ -0,0 +1,6 @@
+# Stained glass pink
+matchBlocks=95
+metadata=6
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7.png
new file mode 100644
index 00000000..7d0ffb23
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/0.png
new file mode 100644
index 00000000..3f07a5d8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/1.png
new file mode 100644
index 00000000..64fffa3e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/10.png
new file mode 100644
index 00000000..b0c0d27a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/11.png
new file mode 100644
index 00000000..112c6ce9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/16.png
new file mode 100644
index 00000000..5a5d27fd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/17.png
new file mode 100644
index 00000000..9479f0e4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/18.png
new file mode 100644
index 00000000..d5f2a667
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/19.png
new file mode 100644
index 00000000..5c6b9e17
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/2.png
new file mode 100644
index 00000000..131366fa
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/20.png
new file mode 100644
index 00000000..e4ffc2f1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/21.png
new file mode 100644
index 00000000..14cc29c4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/22.png
new file mode 100644
index 00000000..3b41dac0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/23.png
new file mode 100644
index 00000000..58a5125b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/24.png
new file mode 100644
index 00000000..e1a40304
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/25.png
new file mode 100644
index 00000000..736d80c3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/26.png
new file mode 100644
index 00000000..b2b0aa83
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/27.png
new file mode 100644
index 00000000..87549c4e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/3.png
new file mode 100644
index 00000000..f37a2c8c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/32.png
new file mode 100644
index 00000000..09c3d65b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/33.png
new file mode 100644
index 00000000..fa44bdb8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/34.png
new file mode 100644
index 00000000..542b9f92
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/35.png
new file mode 100644
index 00000000..5b157bd4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/36.png
new file mode 100644
index 00000000..6d485e9f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/37.png
new file mode 100644
index 00000000..1b5d8ec4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/38.png
new file mode 100644
index 00000000..a0fe8a15
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/39.png
new file mode 100644
index 00000000..92091b88
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/4.png
new file mode 100644
index 00000000..6d822a4f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/40.png
new file mode 100644
index 00000000..5172ef31
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/41.png
new file mode 100644
index 00000000..a281ced0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/42.png
new file mode 100644
index 00000000..62a50e3a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/43.png
new file mode 100644
index 00000000..8fc8b058
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/48.png
new file mode 100644
index 00000000..a40bbcd3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/49.png
new file mode 100644
index 00000000..5b337fbd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/5.png
new file mode 100644
index 00000000..82fcfc38
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/50.png
new file mode 100644
index 00000000..278e7eb9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/51.png
new file mode 100644
index 00000000..b6f30cc4
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/52.png
new file mode 100644
index 00000000..f0ca09d7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/53.png
new file mode 100644
index 00000000..7396d2c3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/54.png
new file mode 100644
index 00000000..7958f254
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/55.png
new file mode 100644
index 00000000..8dcc4de8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/56.png
new file mode 100644
index 00000000..b5f7d160
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/57.png
new file mode 100644
index 00000000..82acb63a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/58.png
new file mode 100644
index 00000000..c6bfce6c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/6.png
new file mode 100644
index 00000000..ccb1f6ac
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/7.png
new file mode 100644
index 00000000..bf67595f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/8.png
new file mode 100644
index 00000000..01acf189
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/9.png
new file mode 100644
index 00000000..d237ab94
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/glass_gray.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/glass_gray.properties
new file mode 100644
index 00000000..5284f514
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/glass_gray.properties
@@ -0,0 +1,6 @@
+# Stained glass gray
+matchBlocks=95
+metadata=7
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/glass_pane_gray.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/glass_pane_gray.properties
new file mode 100644
index 00000000..1743e877
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/7_glass_gray/glass_pane_gray.properties
@@ -0,0 +1,7 @@
+# Stained glass gray
+matchBlocks=160
+metadata=7
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8.png
new file mode 100644
index 00000000..f9bf0fb1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/0.png
new file mode 100644
index 00000000..84616646
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/1.png
new file mode 100644
index 00000000..12a88d0d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/10.png
new file mode 100644
index 00000000..e2c2673e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/11.png
new file mode 100644
index 00000000..076bdbed
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/16.png
new file mode 100644
index 00000000..f1ce496d
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/17.png
new file mode 100644
index 00000000..ab39de70
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/18.png
new file mode 100644
index 00000000..23231db6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/19.png
new file mode 100644
index 00000000..5034a661
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/2.png
new file mode 100644
index 00000000..fc9e60e1
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/20.png
new file mode 100644
index 00000000..c885d28a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/21.png
new file mode 100644
index 00000000..7db45f01
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/22.png
new file mode 100644
index 00000000..fefa27ce
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/23.png
new file mode 100644
index 00000000..875306d9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/24.png
new file mode 100644
index 00000000..ba8e1ce2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/25.png
new file mode 100644
index 00000000..c244eee2
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/26.png
new file mode 100644
index 00000000..5789108c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/27.png
new file mode 100644
index 00000000..13cd6f99
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/3.png
new file mode 100644
index 00000000..9f227223
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/32.png
new file mode 100644
index 00000000..f9f64d6ce
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/33.png
new file mode 100644
index 00000000..ab006a36
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/34.png
new file mode 100644
index 00000000..08dd059f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/35.png
new file mode 100644
index 00000000..78ccad09
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/36.png
new file mode 100644
index 00000000..4606e7a5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/37.png
new file mode 100644
index 00000000..a6f7531a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/38.png
new file mode 100644
index 00000000..bce4bcbb
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/39.png
new file mode 100644
index 00000000..3f3b0ec3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/4.png
new file mode 100644
index 00000000..b93f9cd8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/40.png
new file mode 100644
index 00000000..662e2006
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/41.png
new file mode 100644
index 00000000..8fe077a5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/42.png
new file mode 100644
index 00000000..007d225c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/43.png
new file mode 100644
index 00000000..9521d9d3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/48.png
new file mode 100644
index 00000000..3a492b80
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/49.png
new file mode 100644
index 00000000..f81553a9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/5.png
new file mode 100644
index 00000000..b99c20e3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/50.png
new file mode 100644
index 00000000..3749f750
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/51.png
new file mode 100644
index 00000000..a40c84f5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/52.png
new file mode 100644
index 00000000..99d2700f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/53.png
new file mode 100644
index 00000000..1c5770db
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/54.png
new file mode 100644
index 00000000..7f80e647
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/55.png
new file mode 100644
index 00000000..c003c7f9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/56.png
new file mode 100644
index 00000000..c28a1315
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/57.png
new file mode 100644
index 00000000..84540bc9
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/58.png
new file mode 100644
index 00000000..5544bc91
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/6.png
new file mode 100644
index 00000000..1447715e
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/7.png
new file mode 100644
index 00000000..0e5a7a70
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/8.png
new file mode 100644
index 00000000..fe3a46f3
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/9.png
new file mode 100644
index 00000000..267f2aec
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/glass_pane_silver.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/glass_pane_silver.properties
new file mode 100644
index 00000000..aa47028c
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/glass_pane_silver.properties
@@ -0,0 +1,7 @@
+# Stained glass silver
+matchBlocks=160
+metadata=8
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/glass_silver.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/glass_silver.properties
new file mode 100644
index 00000000..d2696f7a
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/8_glass_silver/glass_silver.properties
@@ -0,0 +1,6 @@
+# Stained glass silver
+matchBlocks=95
+metadata=8
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9.png
new file mode 100644
index 00000000..d4f7c4ec
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/0.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/0.png
new file mode 100644
index 00000000..d30caa44
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/0.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/1.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/1.png
new file mode 100644
index 00000000..941354da
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/1.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/10.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/10.png
new file mode 100644
index 00000000..e6e23b0b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/10.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/11.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/11.png
new file mode 100644
index 00000000..f0eddb73
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/11.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/16.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/16.png
new file mode 100644
index 00000000..fa7272e6
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/16.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/17.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/17.png
new file mode 100644
index 00000000..beebe55c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/17.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/18.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/18.png
new file mode 100644
index 00000000..59dd8a8a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/18.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/19.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/19.png
new file mode 100644
index 00000000..4961e39a
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/19.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/2.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/2.png
new file mode 100644
index 00000000..771ff3b5
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/2.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/20.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/20.png
new file mode 100644
index 00000000..e8c31343
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/20.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/21.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/21.png
new file mode 100644
index 00000000..bd9b84ae
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/21.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/22.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/22.png
new file mode 100644
index 00000000..98f227d0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/22.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/23.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/23.png
new file mode 100644
index 00000000..dc7929ed
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/23.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/24.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/24.png
new file mode 100644
index 00000000..c6c02d95
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/24.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/25.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/25.png
new file mode 100644
index 00000000..7c5e5ba0
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/25.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/26.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/26.png
new file mode 100644
index 00000000..b45b3865
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/26.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/27.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/27.png
new file mode 100644
index 00000000..ba47110f
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/27.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/3.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/3.png
new file mode 100644
index 00000000..81db31fa
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/3.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/32.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/32.png
new file mode 100644
index 00000000..9cc6fda8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/32.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/33.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/33.png
new file mode 100644
index 00000000..97c68604
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/33.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/34.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/34.png
new file mode 100644
index 00000000..8790bd3c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/34.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/35.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/35.png
new file mode 100644
index 00000000..36aafffd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/35.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/36.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/36.png
new file mode 100644
index 00000000..533ce596
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/36.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/37.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/37.png
new file mode 100644
index 00000000..21ea0a16
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/37.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/38.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/38.png
new file mode 100644
index 00000000..c804c82b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/38.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/39.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/39.png
new file mode 100644
index 00000000..497a1ffe
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/39.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/4.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/4.png
new file mode 100644
index 00000000..b1a0f025
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/4.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/40.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/40.png
new file mode 100644
index 00000000..ab09ebde
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/40.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/41.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/41.png
new file mode 100644
index 00000000..d65c5de8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/41.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/42.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/42.png
new file mode 100644
index 00000000..92184c18
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/42.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/43.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/43.png
new file mode 100644
index 00000000..fb3587a8
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/43.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/48.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/48.png
new file mode 100644
index 00000000..f6d84b81
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/48.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/49.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/49.png
new file mode 100644
index 00000000..9016a5fd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/49.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/5.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/5.png
new file mode 100644
index 00000000..44692772
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/5.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/50.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/50.png
new file mode 100644
index 00000000..bba832bf
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/50.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/51.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/51.png
new file mode 100644
index 00000000..706c0bce
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/51.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/52.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/52.png
new file mode 100644
index 00000000..6dc03620
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/52.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/53.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/53.png
new file mode 100644
index 00000000..1fcc91ab
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/53.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/54.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/54.png
new file mode 100644
index 00000000..f1cfbd9b
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/54.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/55.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/55.png
new file mode 100644
index 00000000..aec8126c
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/55.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/56.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/56.png
new file mode 100644
index 00000000..0ddd6691
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/56.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/57.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/57.png
new file mode 100644
index 00000000..7c7bc745
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/57.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/58.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/58.png
new file mode 100644
index 00000000..6495fef7
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/58.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/6.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/6.png
new file mode 100644
index 00000000..663a5d12
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/6.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/7.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/7.png
new file mode 100644
index 00000000..9a120cbd
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/7.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/8.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/8.png
new file mode 100644
index 00000000..39755b87
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/8.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/9.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/9.png
new file mode 100644
index 00000000..74bddb44
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/9.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/glass_cyan.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/glass_cyan.properties
new file mode 100644
index 00000000..807c2d40
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/glass_cyan.properties
@@ -0,0 +1,6 @@
+# Stained glass cyan
+matchBlocks=95
+metadata=9
+connect=block
+method=ctm
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/glass_pane_cyan.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/glass_pane_cyan.properties
new file mode 100644
index 00000000..99df8931
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/9_glass_cyan/glass_pane_cyan.properties
@@ -0,0 +1,7 @@
+# Stained glass cyan
+matchBlocks=160
+metadata=9
+connect=block
+method=ctm
+faces=sides
+tiles=0-11 16-27 32-43 48-58
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/bookshelf.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/bookshelf.properties
new file mode 100644
index 00000000..fd7167c1
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/bookshelf.properties
@@ -0,0 +1,7 @@
+# Converted from /ctm.png
+# Individual tiles are in /ctm/default
+matchBlocks=47
+method=horizontal
+tiles=12-15
+connect=block
+faces=sides
\ No newline at end of file
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/empty.png b/sources/resources/assets/minecraft/mcpatcher/ctm/default/empty.png
new file mode 100644
index 00000000..dbaa21ce
Binary files /dev/null and b/sources/resources/assets/minecraft/mcpatcher/ctm/default/empty.png differ
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/glass.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/glass.properties
new file mode 100644
index 00000000..8064c63b
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/glass.properties
@@ -0,0 +1,6 @@
+# Converted from /ctm.png
+# Individual tiles are in /ctm/default
+matchBlocks=20
+method=ctm
+tiles=0-11 16-27 32-43 48-58
+connect=block
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/glasspane.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/glasspane.properties
new file mode 100644
index 00000000..64b0657d
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/glasspane.properties
@@ -0,0 +1,7 @@
+# Converted from /ctm.png
+# Individual tiles are in /ctm/default
+matchBlocks=102
+method=ctm
+tiles=0-11 16-27 32-43 48-58
+connect=block
+faces=sides
\ No newline at end of file
diff --git a/sources/resources/assets/minecraft/mcpatcher/ctm/default/sandstone.properties b/sources/resources/assets/minecraft/mcpatcher/ctm/default/sandstone.properties
new file mode 100644
index 00000000..9176eb9c
--- /dev/null
+++ b/sources/resources/assets/minecraft/mcpatcher/ctm/default/sandstone.properties
@@ -0,0 +1,7 @@
+# Converted from /ctm.png
+# Individual tiles are in /ctm/default
+matchTiles=sandstone_normal
+metadata=0
+method=top
+tiles=66
+connect=tile
diff --git a/sources/resources/assets/minecraft/optifine/_property_files_index.json b/sources/resources/assets/minecraft/optifine/_property_files_index.json
new file mode 100644
index 00000000..7ff976ba
--- /dev/null
+++ b/sources/resources/assets/minecraft/optifine/_property_files_index.json
@@ -0,0 +1,41 @@
+{
+	"propertyFiles": [
+		"optifine/bettergrass.properties",
+		"mcpatcher/ctm/default/bookshelf.properties",
+		"mcpatcher/ctm/default/glass.properties",
+		"mcpatcher/ctm/default/glasspane.properties",
+		"mcpatcher/ctm/default/sandstone.properties",
+		"mcpatcher/ctm/default/0_glass_white/glass_pane_white.properties",
+		"mcpatcher/ctm/default/0_glass_white/glass_white.properties",
+		"mcpatcher/ctm/default/1_glass_orange/glass_pane_orange.properties",
+		"mcpatcher/ctm/default/1_glass_orange/glass_orange.properties",
+		"mcpatcher/ctm/default/2_glass_magenta/glass_pane_magenta.properties",
+		"mcpatcher/ctm/default/2_glass_magenta/glass_magenta.properties",
+		"mcpatcher/ctm/default/3_glass_light_blue/glass_pane_light_blue.properties",
+		"mcpatcher/ctm/default/3_glass_light_blue/glass_light_blue.properties",
+		"mcpatcher/ctm/default/4_glass_yellow/glass_pane_yellow.properties",
+		"mcpatcher/ctm/default/4_glass_yellow/glass_yellow.properties",
+		"mcpatcher/ctm/default/5_glass_lime/glass_pane_lime.properties",
+		"mcpatcher/ctm/default/5_glass_lime/glass_lime.properties",
+		"mcpatcher/ctm/default/6_glass_pink/glass_pane_pink.properties",
+		"mcpatcher/ctm/default/6_glass_pink/glass_pink.properties",
+		"mcpatcher/ctm/default/7_glass_gray/glass_pane_gray.properties",
+		"mcpatcher/ctm/default/7_glass_gray/glass_gray.properties",
+		"mcpatcher/ctm/default/8_glass_silver/glass_pane_silver.properties",
+		"mcpatcher/ctm/default/8_glass_silver/glass_silver.properties",
+		"mcpatcher/ctm/default/9_glass_cyan/glass_pane_cyan.properties",
+		"mcpatcher/ctm/default/9_glass_cyan/glass_cyan.properties",
+		"mcpatcher/ctm/default/10_glass_purple/glass_pane_purple.properties",
+		"mcpatcher/ctm/default/10_glass_purple/glass_purple.properties",
+		"mcpatcher/ctm/default/11_glass_blue/glass_pane_blue.properties",
+		"mcpatcher/ctm/default/11_glass_blue/glass_blue.properties",
+		"mcpatcher/ctm/default/12_glass_brown/glass_pane_brown.properties",
+		"mcpatcher/ctm/default/12_glass_brown/glass_brown.properties",
+		"mcpatcher/ctm/default/13_glass_green/glass_pane_green.properties",
+		"mcpatcher/ctm/default/13_glass_green/glass_green.properties",
+		"mcpatcher/ctm/default/14_glass_red/glass_pane_red.properties",
+		"mcpatcher/ctm/default/14_glass_red/glass_red.properties",
+		"mcpatcher/ctm/default/15_glass_black/glass_pane_black.properties",
+		"mcpatcher/ctm/default/15_glass_black/glass_black.properties"
+	]
+}
\ No newline at end of file
diff --git a/sources/resources/assets/minecraft/optifine/bettergrass.properties b/sources/resources/assets/minecraft/optifine/bettergrass.properties
new file mode 100644
index 00000000..6964b282
--- /dev/null
+++ b/sources/resources/assets/minecraft/optifine/bettergrass.properties
@@ -0,0 +1,33 @@
+# Configuration for OptiFine's Better Grass feature
+# Location: /assets/minecraft/optifine/bettergrass.properties
+
+# Blocks
+# Enable Better Grass for specific blocks
+grass=true
+mycelium=true
+podzol=true
+
+# Snowy blocks
+# Enable Better Grass for specific blocks which have snow on top
+grass.snow=true
+mycelium.snow=true
+podzol.snow=true
+
+# Multilayer grass sides
+# - layer 1 = grass_side
+# - layer 2 = grass (colored by biome)
+# Allows transparent grass texture to be used as overlay for the grass side
+grass.multilayer=false
+
+# Textures
+# Configure which textures to be used 
+# The "texture.grass" is colored by biome
+texture.grass=blocks/grass_top
+texture.grass_side=blocks/grass_side
+texture.mycelium=blocks/mycelium_top
+texture.podzol=blocks/dirt_podzol_top
+texture.snow=blocks/snow
+
+
+
+
diff --git a/sources/setup/workspace_template/wasm_gc_teavm/javascript/epw_meta.txt b/sources/setup/workspace_template/wasm_gc_teavm/javascript/epw_meta.txt
index c42fc19e..cca1d334 100644
--- a/sources/setup/workspace_template/wasm_gc_teavm/javascript/epw_meta.txt
+++ b/sources/setup/workspace_template/wasm_gc_teavm/javascript/epw_meta.txt
@@ -1,8 +1,8 @@
-client-version-integer=47
+client-version-integer=48
 client-package-name=net.lax1dude.eaglercraft.v1_8.client
 client-origin-name=EaglercraftX
-client-origin-version=u47
+client-origin-version=u48
 client-origin-vendor=lax1dude
 client-fork-name=EaglercraftX
-client-fork-version=u47
+client-fork-version=u48
 client-fork-vendor=lax1dude
diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
index ea3bb55a..e94e021b 100644
--- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
+++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
@@ -77,6 +77,8 @@ class OpenGLObjects {
 		private static int hashGen = 0;
 		final WebGLTexture ptr;
 		final int hash;
+		int width;
+		int height;
 
 		TextureGL(WebGLTexture ptr) {
 			this.ptr = ptr;
@@ -92,6 +94,22 @@ class OpenGLObjects {
 			PlatformOpenGL._wglDeleteTextures(this);
 		}
 
+		@Override
+		public void setCacheSize(int w, int h) {
+			width = w;
+			height = h;
+		}
+
+		@Override
+		public int getWidth() {
+			return width;
+		}
+
+		@Override
+		public int getHeight() {
+			return height;
+		}
+
 	}
 
 	static class ProgramGL implements IProgramGL {
diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java
index 4251b580..2345ea63 100644
--- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java
+++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java
@@ -318,6 +318,7 @@ public class PlatformInput {
 		parent.addEventListener("contextmenu", contextmenu = new EventListener<MouseEvent>() {
 			@Override
 			public void handleEvent(MouseEvent evt) {
+				if(evt.getTarget() == ClientMain.integratedServerCrashPanel) return;
 				evt.preventDefault();
 				evt.stopPropagation();
 			}
@@ -471,8 +472,10 @@ public class PlatformInput {
 		win.addEventListener("keydown", keydown = new EventListener<KeyboardEvent>() {
 			@Override
 			public void handleEvent(KeyboardEvent evt) {
-				evt.preventDefault();
-				evt.stopPropagation();
+				if(!ClientMain.integratedServerCrashPanelShowing) {
+					evt.preventDefault();
+					evt.stopPropagation();
+				}
 				if(!enableRepeatEvents && evt.isRepeat()) return;
 				LegacyKeycodeTranslator.LegacyKeycode keyCode = null;
 				if(keyCodeTranslatorMap != null && hasCodeVar(evt)) {
@@ -528,8 +531,10 @@ public class PlatformInput {
 		win.addEventListener("keyup", keyup = new EventListener<KeyboardEvent>() {
 			@Override
 			public void handleEvent(KeyboardEvent evt) {
-				evt.preventDefault();
-				evt.stopPropagation();
+				if(!ClientMain.integratedServerCrashPanelShowing) {
+					evt.preventDefault();
+					evt.stopPropagation();
+				}
 				LegacyKeycodeTranslator.LegacyKeycode keyCode = null;
 				if(keyCodeTranslatorMap != null && hasCodeVar(evt)) {
 					keyCode = keyCodeTranslatorMap.get(evt.getCode());
diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java
index 8d859ae6..217d4eee 100644
--- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java
+++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java
@@ -618,7 +618,8 @@ public class ClientMain {
 		}
 	}
 
-	private static HTMLElement integratedServerCrashPanel = null;
+	public static HTMLElement integratedServerCrashPanel = null;
+	public static boolean integratedServerCrashPanelShowing = false;
 
 	public static void showIntegratedServerCrashReportOverlay(String report, int x, int y, int w, int h) {
 		if(integratedServerCrashPanel == null) {
@@ -655,12 +656,14 @@ public class ClientMain {
 		style.setProperty("width", "" + ((w / s) - 20) + "px");
 		style.setProperty("height", "" + ((h / s) - 20) + "px");
 		style.setProperty("display", "block");
+		integratedServerCrashPanelShowing = true;
 	}
 
 	public static void hideIntegratedServerCrashReportOverlay() {
 		if(integratedServerCrashPanel != null) {
 			integratedServerCrashPanel.getStyle().setProperty("display", "none");
 		}
+		integratedServerCrashPanelShowing = false;
 	}
 
 	@JSBody(params = { "el", "str" }, script = "el.innerText = str;")
diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
index 5d221ca3..1722c7c9 100644
--- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
+++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
@@ -77,6 +77,8 @@ class OpenGLObjects {
 		private static int hashGen = 0;
 		final WebGLTexture ptr;
 		final int hash;
+		int width;
+		int height;
 
 		TextureGL(WebGLTexture ptr) {
 			this.ptr = ptr;
@@ -92,6 +94,22 @@ class OpenGLObjects {
 			PlatformOpenGL._wglDeleteTextures(this);
 		}
 
+		@Override
+		public void setCacheSize(int w, int h) {
+			width = w;
+			height = h;
+		}
+
+		@Override
+		public int getWidth() {
+			return width;
+		}
+
+		@Override
+		public int getHeight() {
+			return height;
+		}
+
 	}
 
 	static class ProgramGL implements IProgramGL {
diff --git a/sources/wasm-gc-teavm/js/clientPlatformSingleplayer.js b/sources/wasm-gc-teavm/js/clientPlatformSingleplayer.js
index 9bca8aea..c0142cc0 100644
--- a/sources/wasm-gc-teavm/js/clientPlatformSingleplayer.js
+++ b/sources/wasm-gc-teavm/js/clientPlatformSingleplayer.js
@@ -16,6 +16,11 @@
 
 const clientPlatfSPName = "clientPlatformSingleplayer";
 
+/** @type {HTMLElement} */
+var integratedServerCrashPanel = null;
+
+var integratedServerCrashPanelShowing = false;
+
 function initializeClientPlatfSP(spImports) {
 
 	/** @type {Worker|null} */
@@ -163,9 +168,6 @@ function initializeClientPlatfSP(spImports) {
 		}
 	};
 
-	/** @type {HTMLElement} */
-	var integratedServerCrashPanel = null;
-
 	/**
 	 * @param {string} report
 	 * @param {number} x
@@ -188,12 +190,14 @@ function initializeClientPlatfSP(spImports) {
 		integratedServerCrashPanel.style.width = "" + ((w / s) - 20) + "px";
 		integratedServerCrashPanel.style.height = "" + ((h / s) - 20) + "px";
 		integratedServerCrashPanel.style.display = "block";
+		integratedServerCrashPanelShowing = true;
 	};
 
 	spImports["hideCrashReportOverlay"] = function() {
 		if(integratedServerCrashPanel) {
 			integratedServerCrashPanel.style.display = "none";
 		}
+		integratedServerCrashPanelShowing = false;
 	};
 
 	window.__curEaglerX188UnloadListenerCB = function() {
diff --git a/sources/wasm-gc-teavm/js/platformInput.js b/sources/wasm-gc-teavm/js/platformInput.js
index 83bb1b4c..18ae54ee 100644
--- a/sources/wasm-gc-teavm/js/platformInput.js
+++ b/sources/wasm-gc-teavm/js/platformInput.js
@@ -222,6 +222,7 @@ async function initPlatformInput(inputImports) {
 	reportWindowSize();
 
 	parentElement.addEventListener("contextmenu", currentEventListeners.contextmenu = function(/** Event */ evt) {
+		if(evt.target === integratedServerCrashPanel) return;
 		evt.preventDefault();
 		evt.stopPropagation();
 	});
@@ -369,8 +370,10 @@ async function initPlatformInput(inputImports) {
 	}
 
 	window.addEventListener("keydown", /** @type {function(Event)} */ (currentEventListeners.keydown = function(/** KeyboardEvent */ evt) {
-		evt.preventDefault();
-		evt.stopPropagation();
+		if(integratedServerCrashPanelShowing) {
+			evt.preventDefault();
+			evt.stopPropagation();
+		}
 		if(evt.key === "F11" && !evt.repeat) {
 			toggleFullscreenImpl();
 			return;
@@ -380,8 +383,10 @@ async function initPlatformInput(inputImports) {
 	}));
 
 	window.addEventListener("keyup", /** @type {function(Event)} */ (currentEventListeners.keyup = function(/** KeyboardEvent */ evt) {
-		evt.preventDefault();
-		evt.stopPropagation();
+		if(integratedServerCrashPanelShowing) {
+			evt.preventDefault();
+			evt.stopPropagation();
+		}
 		pushEvent(EVENT_TYPE_INPUT, EVENT_INPUT_KEYBOARD, createKeyEvent(EVENT_KEY_UP, evt));
 	}));