--- ../src-base/minecraft/org/bukkit/Material.java
+++ ../src-work/minecraft/org/bukkit/Material.java
@@ -3,6 +3,22 @@
 import java.lang.reflect.Constructor;
 import java.util.Map;
 
+// Cauldron start
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.bukkit.inventory.ItemStack;
+
+import net.minecraftforge.common.util.EnumHelper;
+import net.minecraftforge.cauldron.api.inventory.BukkitOreDictionary;
+import net.minecraftforge.cauldron.api.inventory.OreDictionaryEntry;
+// Cauldron end
+
 import org.apache.commons.lang.Validate;
 import org.bukkit.map.MapView;
 import org.bukkit.material.Bed;
@@ -418,14 +434,30 @@
     private final int id;
     private final Constructor<? extends MaterialData> ctor;
     private static Material[] byId = new Material[383];
-    private final static Map<String, Material> BY_NAME = Maps.newHashMap();
+    private static Map<String, Material> BY_NAME = Maps.newHashMap(); // Cauldron - remove final
     private final int maxStack;
     private final short durability;
+    // Cauldron start
+    private static Object reflectionFactory;
+    private static Method newConstructorAccessor;
+    private static Method newInstance;
+    private static Method newFieldAccessor;
+    private static Method fieldAccessorSet;
+    private static boolean isSetup;
+    private boolean isForgeBlock = false;
+    // Cauldron end
 
     private Material(final int id) {
         this(id, 64);
     }
 
+    // Cauldron start - constructor used to set if the Material is a block or not
+    private Material(final int id, boolean flag) {
+        this(id, 64);
+        this.isForgeBlock = flag;
+    }
+    // Cauldron end
+
     private Material(final int id, final int stack) {
         this(id, stack, MaterialData.class);
     }
@@ -526,7 +558,7 @@
      * @return true if this material is a block
      */
     public boolean isBlock() {
-        return id < 256;
+        return id < 256 || isForgeBlock; // Cauldron
     }
 
     /**
@@ -615,16 +647,202 @@
         } catch (NumberFormatException ex) {}
 
         if (result == null) {
-            String filtered = name.toUpperCase();
-
-            filtered = filtered.replaceAll("\\s+", "_").replaceAll("\\W", "");
+            // Cauldron start - extract to normalizeName()
+            String filtered = normalizeName(name);
             result = BY_NAME.get(filtered);
+            // Cauldron end
         }
 
+        // Cauldron start - Try the ore dictionary
+        if (result == null) {
+            BukkitOreDictionary dict = net.minecraftforge.cauldron.api.Cauldron.getOreDictionary();
+            OreDictionaryEntry entry = dict.getOreEntry(name);
+            if (entry != null) {
+                List<ItemStack> items = dict.getDefinitions(entry);
+                if (items.size() > 0) {
+                    // TODO check sanity on multiple item results
+                    ItemStack item = items.get(0);
+                    if (item.getDurability() == 0 || item.getDurability() == Short.MAX_VALUE) {
+                        result = item.getType();
+                    } else {
+                        // bad! we have an item with data!
+                    }
+                }
+            }
+        }
+        // Cauldron end
+
         return result;
     }
 
+    /* ===============================  Cauldron START ============================= */
+
+    // use a normalize() function to ensure it is accessible after a round-trip
+    public static String normalizeName(String name) {
+        return name.toUpperCase().replaceAll("(:|\\s)", "_").replaceAll("\\W", "");
+    }
+
+    public static Material addMaterial(int id, boolean isBlock)
+    {
+        return addMaterial(id, "X" + String.valueOf(id), isBlock);
+    }
+
+    public static Material addMaterial(int id, String name, boolean isBlock) {
+      if (byId[id] == null) {
+        String materialName = normalizeName(name);
+        Material material = (Material) EnumHelper.addEnum(Material.class, materialName, new Class[]{Integer.TYPE, Boolean.TYPE}, new Object[]{Integer.valueOf(id), isBlock});
+        byId[id] = material;
+        BY_NAME.put(materialName, material);
+        BY_NAME.put("X" + String.valueOf(id), material);
+        return material;
+      }
+      return null;
+    }
+
+    public static void setMaterialName(int id, String name, boolean flag) {
+      String materialName = normalizeName(name);
+
+      if (byId[id] == null)
+      {
+        addMaterial(id, materialName, flag);
+      }
+      else // replace existing enum
+      {
+          /* TODO: find out how to do this with Forge's EnumHelper (addEnum?) - used for enabling descriptive (vs numeric) Material names
+          Material material = getMaterial(id);
+          BY_NAME.remove(material);
+          Material newMaterial = EnumHelper.replaceEnum(Material.class, material_name, material.ordinal(), new Class[] { Integer.TYPE }, new Object[] { Integer.valueOf(id) });
+          if (newMaterial == null)
+              System.out.println("Error replacing Material " + name + " with id " + id);
+          else {
+              byId[id] = newMaterial;
+              BY_NAME.put(material_name, newMaterial);
+          }
+          */
+      }
+    }
+
+    private static void setup()
+    {
+      if (isSetup)
+      {
+        return;
+      }
+      try {
+        Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory", new Class[0]);
+        reflectionFactory = getReflectionFactory.invoke(null, new Object[0]);
+        newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", new Class[] { Constructor.class });
+        newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", new Class[] { Object[].class });
+        newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", new Class[] { Field.class, Boolean.TYPE });
+        fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", new Class[] { Object.class, Object.class });
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+
+      isSetup = true;
+    }
+
+    private static Object getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes) throws Exception {
+      Class[] parameterTypes = null;
+
+      parameterTypes = new Class[additionalParameterTypes.length + 2];
+      parameterTypes[0] = String.class;
+      parameterTypes[1] = Integer.TYPE;
+      System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
+
+      return newConstructorAccessor.invoke(reflectionFactory, new Object[] { enumClass.getDeclaredConstructor(parameterTypes) });
+    }
+
+    private static <T extends Enum<?>> T makeEnum(Class<T> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues) throws Exception {
+      Object[] parms = null;
+
+      parms = new Object[additionalValues.length + 2];
+      parms[0] = value;
+      parms[1] = Integer.valueOf(ordinal);
+      System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
+
+      return (T)enumClass.cast(newInstance.invoke(getConstructorAccessor(enumClass, additionalTypes), new Object[] { parms }));
+    }
+
+    private static void setFailsafeFieldValue(Field field, Object target, Object value) throws Exception {
+      field.setAccessible(true);
+      Field modifiersField = Field.class.getDeclaredField("modifiers");
+      modifiersField.setAccessible(true);
+      modifiersField.setInt(field, field.getModifiers() & 0xFFFFFFEF);
+      Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, new Object[] { field, Boolean.valueOf(false) });
+      fieldAccessorSet.invoke(fieldAccessor, new Object[] { target, value });
+    }
+
+    private static void blankField(Class<?> enumClass, String fieldName) throws Exception {
+      for (Field field : Class.class.getDeclaredFields())
+        if (field.getName().contains(fieldName)) {
+          field.setAccessible(true);
+          setFailsafeFieldValue(field, enumClass, null);
+          break;
+        }
+    }
+
+    private static void cleanEnumCache(Class<?> enumClass) throws Exception
+    {
+      blankField(enumClass, "enumConstantDirectory");
+      blankField(enumClass, "enumConstants");
+    }
+
+    public static <T extends Enum<?>> T replaceEnum(Class<T> enumType, String enumName, int ordinal,  Class<?>[] paramTypes, Object[] paramValues)
+    {
+      if (!isSetup) setup();
+      Field valuesField = null;
+      Field[] fields = enumType.getDeclaredFields();
+      int flags = 4122;
+      String valueType = String.format("[L%s;", new Object[] { enumType.getName() });
+
+      for (Field field : fields) {
+        if (((field.getModifiers() & flags) != flags) || (!field.getType().getName().equals(valueType))) {
+          continue;
+        }
+        valuesField = field;
+        break;
+      }
+
+      valuesField.setAccessible(true);
+      try
+      {
+        Enum[] previousValues = (Enum[])(Enum[])valuesField.get(enumType);
+        Enum[] newValues = new Enum[previousValues.length];
+        Enum newValue = null;
+        for (Enum enumValue : previousValues)
+        {
+            if (enumValue.ordinal() == ordinal)
+            {
+               newValue = makeEnum(enumType, enumName, ordinal, paramTypes, paramValues);
+               newValues[enumValue.ordinal()] =  newValue;
+            }
+            else newValues[enumValue.ordinal()] = enumValue;
+        }
+        List values = new ArrayList(Arrays.asList(newValues));
+
+        setFailsafeFieldValue(valuesField, null, values.toArray((Enum[])(Enum[])Array.newInstance(enumType, 0)));
+        cleanEnumCache(enumType);
+        return (T) newValue;
+      } catch (Exception e) {
+        e.printStackTrace();
+        throw new RuntimeException(e.getMessage(), e);
+      }
+    }
+    /* ===============================  Cauldron END============================= */
+
     static {
+        // Cauldron start
+        byId = new Material[32000];
+        BY_NAME = Maps.newHashMap();
+
+        reflectionFactory = null;
+        newConstructorAccessor = null;
+        newInstance = null;
+        newFieldAccessor = null;
+        fieldAccessorSet = null;
+        isSetup = false;
+        // Cauldron end
         for (Material material : values()) {
             if (byId.length > material.id) {
                 byId[material.id] = material;
