Package amidst.minecraft

Source Code of amidst.minecraft.Minecraft

package amidst.minecraft;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Stack;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import amidst.Options;
import amidst.Util;
import amidst.bytedata.ByteClass;
import amidst.bytedata.ByteClass.AccessFlags;
import amidst.bytedata.CCLongMatch;
import amidst.bytedata.CCMethodPreset;
import amidst.bytedata.CCMulti;
import amidst.bytedata.CCPropertyPreset;
import amidst.bytedata.CCRequire;
import amidst.bytedata.CCStringMatch;
import amidst.bytedata.CCWildcardByteSearch;
import amidst.bytedata.ClassChecker;
import amidst.json.JarLibrary;
import amidst.json.JarProfile;
import amidst.logging.Log;
import amidst.version.VersionInfo;

public class Minecraft {
  private static final int MAX_CLASSES = 128;
  private Class<?> mainClass;
  private URLClassLoader classLoader;
  private String versionID;
  private URL urlToJar;
  private File jarFile;
 
  private static ClassChecker[] classChecks = new ClassChecker[] {
      new CCWildcardByteSearch("IntCache", DeobfuscationData.intCache),
      new CCStringMatch("WorldType", "default_1_1"),
      new CCLongMatch("GenLayer", 1000L, 2001L, 2000L),
      new CCStringMatch("IntCache", ", tcache: "),
      (new ClassChecker() {
        @Override
        public void check(Minecraft m, ByteClass bClass) {
          if (bClass.fields.length != 3)
            return;
          int privateStatic = AccessFlags.PRIVATE | AccessFlags.STATIC;
          for (int i = 0; i < 3; i++) {
            if ((bClass.fields[i].accessFlags & privateStatic) != privateStatic)
              return;
          }
         
          if ((bClass.constructorCount == 0) && (bClass.methodCount == 6) && (bClass.searchForUtf("isDebugEnabled"))) {
            m.registerClass("BlockInit", bClass);
            isComplete = true;
          }
        }
      }),
      new CCRequire(
        new CCPropertyPreset(
          "WorldType",
          "a", "types",
          "b", "default",
          "c", "flat",
          "d", "largeBiomes",
          "e", "amplified",
          "g", "default_1_1",
          "f", "customized"
        )
      , "WorldType"),
      new CCRequire(
        new CCMethodPreset(
            "BlockInit",
            "c()", "initialize"
          )
      , "BlockInit"),
      new CCRequire(
        new CCMethodPreset(
          "GenLayer",
          "a(long, @WorldType)", "initializeAllBiomeGenerators",
          "a(long, @WorldType, String)", "initializeAllBiomeGeneratorsWithParams",
          "a(int, int, int, int)", "getInts"
        )
      , "GenLayer"),
      new CCRequire(new CCMulti(
        new CCMethodPreset(
          "IntCache",
          "a(int)", "getIntCache",
          "a()", "resetIntCache",
          "b()", "getInformation"
        ),
        new CCPropertyPreset(
          "IntCache",
          "a", "intCacheSize",
          "b","freeSmallArrays",
          "c","inUseSmallArrays",
          "d","freeLargeArrays",
          "e","inUseLargeArrays"
        )
      ), "IntCache")
  };
  private HashMap<String, ByteClass> byteClassMap;
  private HashMap<String, MinecraftClass> nameMap;
  private HashMap<String, MinecraftClass> classMap;
  private Vector<String> byteClassNames;
 
  public String versionId;
  public VersionInfo version = VersionInfo.unknown;
 
  public Minecraft(File jarFilethrows MalformedURLException {
    this.jarFile = jarFile;
    byteClassNames = new Vector<String>();
    byteClassMap = new HashMap<String, ByteClass>(MAX_CLASSES);
    urlToJar = jarFile.toURI().toURL();
   
    Log.i("Reading minecraft.jar...");
    if (!jarFile.exists())
      Log.crash("Attempted to load jar file at: " + jarFile + " but it does not exist.");
    Stack<ByteClass> byteClassStack = new Stack<ByteClass>();
    try {
      ZipFile jar = new ZipFile(jarFile);
      Enumeration<? extends ZipEntry> enu = jar.entries();
     
      while (enu.hasMoreElements()) {
        ZipEntry entry = enu.nextElement();
        String currentEntry = entry.getName();
        String[] nameSplit = currentEntry.split("\\.");
        if (!entry.isDirectory() && (nameSplit.length == 2) && (nameSplit[0].indexOf('/') == -1) && nameSplit[1].equals("class")) {
          BufferedInputStream is = new BufferedInputStream(jar.getInputStream(entry));
          if (is.available() < 8000) { // TODO: Double check that this filter won't mess anything up.
            byte[] classData = new byte[is.available()];
            is.read(classData);
            is.close();
            byteClassStack.push(new ByteClass(nameSplit[0], classData));
          }
        }
      }
      jar.close();
      Log.i("Jar load complete.");
    } catch (Exception e) {
      e.printStackTrace();
      Log.crash(e, "Error extracting jar data.");
    }
   
    Log.i("Searching for classes...");
    int checksRemaining = classChecks.length;
    Object[] byteClasses = byteClassStack.toArray();
    boolean[] found = new boolean[byteClasses.length];
    while (checksRemaining != 0) {
      for (int q = 0; q < classChecks.length; q++) {
        for (int i = 0; i < byteClasses.length; i++) {
          if (!found[q]) {
            classChecks[q].check(this, (ByteClass)byteClasses[i]);
            if (classChecks[q].isComplete) {
              Log.debug("Found: " + byteClasses[i] + " as " + classChecks[q].getName() + " | " + classChecks[q].getClass().getSimpleName());
              found[q] = true;
              checksRemaining--;
            }
            // TODO: What is this line, and why is it commented
            //byteClassMap.put(classChecks[q].getName(), classFiles[i].getName().split("\\.")[0]);
          }
        }
        if (!found[q]) {
          classChecks[q].passes--;
          if (classChecks[q].passes == 0) {
            found[q] = true;
            checksRemaining--;
          }
        }
       

      }
    }
    Log.i("Class search complete.");
   
    Log.i("Generating version ID...");
    use();
    try {
      use();
      if (classLoader.findResource("net/minecraft/client/Minecraft.class") != null)
        mainClass = classLoader.loadClass("net.minecraft.client.Minecraft");
      else if (classLoader.findResource("net/minecraft/server/MinecraftServer.class") != null)
        mainClass = classLoader.loadClass("net.minecraft.server.MinecraftServer");
      else
        throw new RuntimeException();
    } catch (Exception e) {
      e.printStackTrace(); // TODO: Make this exception far less broad.
      Log.crash(e, "Attempted to load non-minecraft jar, or unable to locate starting point.");
    }
    String typeDump = "";
    Field fields[] = null;
    try {
      fields = mainClass.getDeclaredFields();
    } catch (NoClassDefFoundError e) {
      e.printStackTrace();
      Log.crash(e, "Unable to find critical external class while loading.\nPlease ensure you have the correct Minecraft libraries installed.");
    }
   
    for (int i = 0; i < fields.length; i++) {
      String typeString = fields[i].getType().toString();
      if (typeString.startsWith("class ") && !typeString.contains("."))
        typeDump += typeString.substring(6);
    }
    versionId = typeDump;
    for (VersionInfo v : VersionInfo.values()) {
      if (versionId.equals(v.versionId)) {
        version = v;
        break;
      }
    }

    Log.i("Identified Minecraft [" + version.name() + "] with versionID of " + versionId);
    Log.i("Loading classes...");
    nameMap = new HashMap<String, MinecraftClass>();
    classMap = new HashMap<String, MinecraftClass>();

    for (String name : byteClassNames) {
      ByteClass byteClass = byteClassMap.get(name);
      MinecraftClass minecraftClass = new MinecraftClass(name, byteClass.getClassName());
      minecraftClass.load(this);
      nameMap.put(minecraftClass.getName(), minecraftClass);
      classMap.put(minecraftClass.getClassName(), minecraftClass);
    }
   
    for (MinecraftClass minecraftClass : nameMap.values()) {
      ByteClass byteClass = byteClassMap.get(minecraftClass.getName());
      for (String[] property : byteClass.getProperties())
        minecraftClass.addProperty(new MinecraftProperty(minecraftClass, property[1], property[0]));
      for (String[] method : byteClass.getMethods()) {
        String methodString = obfuscateStringClasses(method[0]);
        methodString = methodString.replaceAll(",INVALID", "").replaceAll("INVALID,","").replaceAll("INVALID", "");
        String methodDeobfName = method[1];
        String methodObfName = methodString.substring(0, methodString.indexOf('('));
        String parameterString = methodString.substring(methodString.indexOf('(') + 1, methodString.indexOf(')'));
       
        if (parameterString.equals("")) {
          minecraftClass.addMethod(new MinecraftMethod(minecraftClass, methodDeobfName, methodObfName));
        } else {
          String[] parameterClasses = parameterString.split(",");
          minecraftClass.addMethod(new MinecraftMethod(minecraftClass, methodDeobfName, methodObfName, parameterClasses));
        }
      }
      for (String[] constructor : byteClass.getConstructors()) {
        String methodString = obfuscateStringClasses(constructor[0]).replaceAll(",INVALID", "").replaceAll("INVALID,","").replaceAll("INVALID", "");
        String methodDeobfName = constructor[1];
        String methodObfName = methodString.substring(0, methodString.indexOf('('));
        String parameterString = methodString.substring(methodString.indexOf('(') + 1, methodString.indexOf(')'));
       
        if (parameterString.equals("")) {
          minecraftClass.addMethod(new MinecraftMethod(minecraftClass, methodDeobfName, methodObfName));
        } else {
          String[] parameterClasses = parameterString.split(",");
          minecraftClass.addMethod(new MinecraftMethod(minecraftClass, methodDeobfName, methodObfName, parameterClasses));
        }
      }
    }
    Log.i("Classes loaded.");
    Log.i("Minecraft load complete.");
  }
  private String obfuscateStringClasses(String inString) {
    inString = inString.replaceAll(" ", "");
    Pattern cPattern = Pattern.compile("@[A-Za-z]+");
    Matcher cMatcher = cPattern.matcher(inString);
    String tempOutput = inString;
    while (cMatcher.find()) {
      String match = inString.substring(cMatcher.start(), cMatcher.end());
      ByteClass byteClass = getByteClass(match.substring(1));
      if (byteClass != null) {
        tempOutput = tempOutput.replaceAll(match, byteClass.getClassName());
      } else {
        tempOutput = tempOutput.replaceAll(match, "INVALID");
      }
      cMatcher = cPattern.matcher(tempOutput);
    }
    return tempOutput;
  }
 
 
  public URL getPath() {
    return urlToJar;
  }
 
  private Stack<URL> getLibraries(File jsonFile) {
    Log.i("Loading libraries.");
    Stack<URL> libraries = new Stack<URL>();
    JarProfile profile = null;
    try {
      profile = Util.readObject(jsonFile, JarProfile.class);
    } catch (IOException e) {
      Log.w("Invalid jar profile loaded. Library loading will be skipped. (Path: " + jsonFile + ")");
      return libraries;
    }
   
    for (int i = 0; i < profile.libraries.size(); i++) {
      JarLibrary library = profile.libraries.get(i);
      if (library.isActive() && library.getFile() != null && library.getFile().exists()) {
        try {
          libraries.add(library.getFile().toURI().toURL());
          Log.i("Found library: " + library.getFile());
        } catch (MalformedURLException e) {
          Log.w("Unable to convert library file to URL with path: " + library.getFile());
          e.printStackTrace();
        }
      } else {
        Log.i("Skipping library: " + library.name);
      }
    }
   
    return libraries;
  }
 
  /*
   * This was the old search-and-add-all libraries method. This may still be useful
   * if the user doesn't have a json file, or mojang changes the format.
   *
  private Stack<URL> getLibraries(File path, Stack<URL> urls) {
    File[] files = path.listFiles();
    for (int i = 0; i < files.length; i++) {
      if (files[i].isDirectory()) {
        getLibraries(files[i], urls);
      } else {
        try {
          Log.i("Found library: " + files[i]);
          urls.push(files[i].toURI().toURL());
        } catch (MalformedURLException e) {
          e.printStackTrace();
        }
      }
    }
    return urls;
  }
  */
 
  public void use() {
    File librariesJson = Options.instance.minecraftJson == null ?
        new File(jarFile.getPath().replace(".jar", ".json"))
        : new File(Options.instance.minecraftJson);
    if (librariesJson.exists()) {
      Stack<URL> libraries = getLibraries(librariesJson);
      URL[] libraryArray = new URL[libraries.size() + 1];
      libraries.toArray(libraryArray);
      libraryArray[libraries.size()] = urlToJar;
      classLoader = new URLClassLoader(libraryArray);
    } else {
      Log.i("Unable to find Minecraft library JSON at: " + librariesJson + ". Skipping.");
      classLoader = new URLClassLoader(new URL[] { urlToJar });
    }
    Thread.currentThread().setContextClassLoader(classLoader);
  }
 
  public String getVersionID() {
    return versionID;
  }
  public MinecraftClass getClassByName(String name) {
    return nameMap.get(name);
  }
  public URLClassLoader getClassLoader() {
    return classLoader;
  }
  public Class<?> loadClass(String name) {
    try {
      return classLoader.loadClass(name);
    } catch (ClassNotFoundException e) {
      Log.crash(e, "Error loading a class (" + name + ")");
      e.printStackTrace();
    }
    return null;
  }
  public MinecraftClass getClassByType(String name) {
    return classMap.get(name);
   
   
   
  }
  public void registerClass(String publicName, ByteClass bClass) {
    if (byteClassMap.get(publicName)==null) {
      byteClassMap.put(publicName, bClass);
      byteClassNames.add(publicName);
    }
  }
  public ByteClass getByteClass(String name) {
    return byteClassMap.get(name);
  }
 
  public IMinecraftInterface createInterface() {
    return new LocalMinecraftInterface(this);
  }
 
}
TOP

Related Classes of amidst.minecraft.Minecraft

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