private Map<String,String> getFieldMap(String className, boolean raw) { if (raw) { return getRawFieldMap(className); } if (!fieldNameMaps.containsKey(className) && !negativeCacheFields.contains(className)) { findAndMergeSuperMaps(className); if (!fieldNameMaps.containsKey(className)) { negativeCacheFields.add(className); } if (DUMP_FIELD_MAPS) { FMLRelaunchLog.finer("Field map for %s : %s", className, fieldNameMaps.get(className)); } } return fieldNameMaps.get(className); }
private Map<String,String> getMethodMap(String className) { if (!methodNameMaps.containsKey(className) && !negativeCacheMethods.contains(className)) { findAndMergeSuperMaps(className); if (!methodNameMaps.containsKey(className)) { negativeCacheMethods.add(className); } if (DUMP_METHOD_MAPS) { FMLRelaunchLog.finer("Method map for %s : %s", className, methodNameMaps.get(className)); } } return methodNameMaps.get(className); }
private void stripMethod(ClassNode classNode, String methodDescriptor) { if(classNode.name.endsWith("$class")) { String subName = classNode.name.substring(0, classNode.name.length() - 6); int pos = methodDescriptor.indexOf('(') + 1; methodDescriptor = methodDescriptor.substring(0, pos) + 'L' + subName + ';' + methodDescriptor.substring(pos); } for (ListIterator<MethodNode> iterator = classNode.methods.listIterator(); iterator.hasNext();) { MethodNode method = iterator.next(); if (methodDescriptor.equals(method.name+method.desc)) { iterator.remove(); if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - method %s removed", methodDescriptor); return; } } if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - method %s NOT removed - not found", methodDescriptor); }
@SuppressWarnings("unchecked") public ModAccessTransformer() throws Exception { super(ModAccessTransformer.class); //We are in the new ClassLoader here, so we need to get the static field from the other ClassLoader. ClassLoader classLoader = this.getClass().getClassLoader().getClass().getClassLoader(); //Bit odd but it gets the class loader that loaded our current class loader yay java! Class<?> otherClazz = Class.forName(this.getClass().getName(), true, classLoader); Field otherField = otherClazz.getDeclaredField("embedded"); otherField.setAccessible(true); embedded = (Map<String, String>)otherField.get(null); for (Map.Entry<String, String> e : embedded.entrySet()) { int old_count = getModifiers().size(); processATFile(CharSource.wrap(e.getValue())); int added = getModifiers().size() - old_count; if (added > 0) { FMLRelaunchLog.fine("Loaded %d rules from AccessTransformer mod jar file %s\n", added, e.getKey()); } } }
private ClassPatchManager() { if (dumpPatched) { tempDir = Files.createTempDir(); FMLRelaunchLog.info("Dumping patched classes to %s",tempDir.getAbsolutePath()); } }
private ClassPatch readPatch(JarEntry patchEntry, JarInputStream jis) { if (DEBUG) FMLRelaunchLog.finer("Reading patch data from %s", patchEntry.getName()); ByteArrayDataInput input; try { input = ByteStreams.newDataInput(ByteStreams.toByteArray(jis)); } catch (IOException e) { FMLRelaunchLog.log(Level.WARN, e, "Unable to read binpatch file %s - ignoring", patchEntry.getName()); return null; } String name = input.readUTF(); String sourceClassName = input.readUTF(); String targetClassName = input.readUTF(); boolean exists = input.readBoolean(); int inputChecksum = 0; if (exists) { inputChecksum = input.readInt(); } int patchLength = input.readInt(); byte[] patchBytes = new byte[patchLength]; input.readFully(patchBytes); return new ClassPatch(name, sourceClassName, targetClassName, exists, inputChecksum, patchBytes); }
@Override public void injectIntoClassLoader(LaunchClassLoader classLoader) { // Deobfuscation transformer, always last, and the access transformer tweaker as well classLoader.registerTransformer("net.minecraftforge.fml.common.asm.transformers.DeobfuscationTransformer"); // Add all the access transformers now as well for (String transformer : CoreModManager.getAccessTransformers()) { classLoader.registerTransformer(transformer); } classLoader.registerTransformer("net.minecraftforge.fml.common.asm.transformers.ModAccessTransformer"); classLoader.registerTransformer("net.minecraftforge.fml.common.asm.transformers.ItemStackTransformer"); try { FMLRelaunchLog.fine("Validating minecraft"); Class<?> loaderClazz = Class.forName("net.minecraftforge.fml.common.Loader", true, classLoader); Method m = loaderClazz.getMethod("injectData", Object[].class); m.invoke(null, (Object)FMLInjectionData.data()); m = loaderClazz.getMethod("instance"); m.invoke(null); FMLRelaunchLog.fine("Minecraft validated, launching..."); } catch (Exception e) { // Load in the Loader, make sure he's ready to roll - this will initialize most of the rest of minecraft here System.out.println("A CRITICAL PROBLEM OCCURRED INITIALIZING MINECRAFT - LIKELY YOU HAVE AN INCORRECT VERSION FOR THIS FML"); throw new RuntimeException(e); } }
public void setupLoadOnly(String deobfFileName, boolean loadAll) { try { File mapData = new File(deobfFileName); LZMAInputSupplier zis = new LZMAInputSupplier(new FileInputStream(mapData)); CharSource srgSource = zis.asCharSource(Charsets.UTF_8); List<String> srgList = srgSource.readLines(); rawMethodMaps = Maps.newHashMap(); rawFieldMaps = Maps.newHashMap(); Builder<String, String> builder = ImmutableBiMap.builder(); Splitter splitter = Splitter.on(CharMatcher.anyOf(": ")).omitEmptyStrings().trimResults(); for (String line : srgList) { String[] parts = Iterables.toArray(splitter.split(line),String.class); String typ = parts[0]; if ("CL".equals(typ)) { parseClass(builder, parts); } else if ("MD".equals(typ) && loadAll) { parseMethod(parts); } else if ("FD".equals(typ) && loadAll) { parseField(parts); } } classNameBiMap = builder.build(); } catch (IOException ioe) { FMLRelaunchLog.log(Level.ERROR, "An error occurred loading the deobfuscation map data", ioe); } methodNameMaps = Maps.newHashMapWithExpectedSize(rawMethodMaps.size()); fieldNameMaps = Maps.newHashMapWithExpectedSize(rawFieldMaps.size()); }
private String getFieldType(String owner, String name) { if (fieldDescriptions.containsKey(owner)) { return fieldDescriptions.get(owner).get(name); } synchronized (fieldDescriptions) { try { byte[] classBytes = ClassPatchManager.INSTANCE.getPatchedResource(owner, map(owner).replace('/', '.'), classLoader); if (classBytes == null) { return null; } ClassReader cr = new ClassReader(classBytes); ClassNode classNode = new ClassNode(); cr.accept(classNode, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); Map<String,String> resMap = Maps.newHashMap(); for (FieldNode fieldNode : classNode.fields) { resMap.put(fieldNode.name, fieldNode.desc); } fieldDescriptions.put(owner, resMap); return resMap.get(name); } catch (IOException e) { FMLRelaunchLog.log(Level.ERROR,e, "A critical exception occurred reading a class file %s", owner); } return null; } }
private void stripInterface(ClassNode classNode, String interfaceName, boolean stripRefs) { final String ifaceName = interfaceName.replace('.', '/'); boolean found = classNode.interfaces.remove(ifaceName); if (found && classNode.signature != null) { SignatureReader sr = new SignatureReader(classNode.signature); final RemovingSignatureWriter signatureWriter = new RemovingSignatureWriter(ifaceName); sr.accept(signatureWriter); classNode.signature = signatureWriter.toString(); if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - interface %s removed from type signature"); } if (found && logDebugInfo) FMLRelaunchLog.finer("Optional removal - interface %s removed", interfaceName); if (!found && logDebugInfo) FMLRelaunchLog.finer("Optional removal - interface %s NOT removed - not found", interfaceName); if (found && stripRefs) { if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - interface %s - stripping method signature references", interfaceName); for (Iterator<MethodNode> iterator = classNode.methods.iterator(); iterator.hasNext();) { MethodNode node = iterator.next(); if (node.desc.contains(ifaceName)) { if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - interface %s - stripping method containing reference %s", interfaceName, node.name); iterator.remove(); } } if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - interface %s - all method signature references stripped", interfaceName); } else if (found) { if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - interface %s - NOT stripping method signature references", interfaceName); } }
void readMapFile(String rulesFile) throws IOException { File file = new File(rulesFile); URL rulesResource; if (file.exists()) { rulesResource = file.toURI().toURL(); } else { rulesResource = Resources.getResource(rulesFile); } processATFile(Resources.asCharSource(rulesResource, Charsets.UTF_8)); FMLRelaunchLog.fine("Loaded %d rules from AccessTransformer config file %s", modifiers.size(), rulesFile); }
public void setup(File mcDir, LaunchClassLoader classLoader, String deobfFileName) { this.classLoader = classLoader; try { List<String> srgList; final String gradleStartProp = System.getProperty("net.minecraftforge.gradle.GradleStart.srg.srg-mcp"); if (Strings.isNullOrEmpty(gradleStartProp)) { // get as a resource InputStream classData = getClass().getResourceAsStream(deobfFileName); LZMAInputSupplier zis = new LZMAInputSupplier(classData); CharSource srgSource = zis.asCharSource(Charsets.UTF_8); srgList = srgSource.readLines(); FMLRelaunchLog.fine("Loading deobfuscation resource %s with %d records", deobfFileName, srgList.size()); } else { srgList = Files.readLines(new File(gradleStartProp), Charsets.UTF_8); FMLRelaunchLog.fine("Loading deobfuscation resource %s with %d records", gradleStartProp, srgList.size()); } rawMethodMaps = Maps.newHashMap(); rawFieldMaps = Maps.newHashMap(); Builder<String, String> builder = ImmutableBiMap.builder(); Splitter splitter = Splitter.on(CharMatcher.anyOf(": ")).omitEmptyStrings().trimResults(); for (String line : srgList) { String[] parts = Iterables.toArray(splitter.split(line),String.class); String typ = parts[0]; if ("CL".equals(typ)) { parseClass(builder, parts); } else if ("MD".equals(typ)) { parseMethod(parts); } else if ("FD".equals(typ)) { parseField(parts); } } classNameBiMap = builder.build(); } catch (IOException ioe) { FMLRelaunchLog.log(Level.ERROR, ioe, "An error occurred loading the deobfuscation map data"); } methodNameMaps = Maps.newHashMapWithExpectedSize(rawMethodMaps.size()); fieldNameMaps = Maps.newHashMapWithExpectedSize(rawFieldMaps.size()); }
@Override public byte[] transform(String name, String transformedName, byte[] basicClass) { String lookupName = name; if(name.endsWith("$class")) { lookupName = name.substring(0, name.length() - 6); } if (optionals == null || !optionals.containsKey(lookupName)) { return basicClass; } ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - found optionals for class %s - processing", name); for (ASMData optional : optionals.get(lookupName)) { String modId = (String) optional.getAnnotationInfo().get("modid"); if (Loader.isModLoaded(modId) || ModAPIManager.INSTANCE.hasAPI(modId)) { if (logDebugInfo) FMLRelaunchLog.finer("Optional removal skipped - mod present %s", modId); continue; } if (logDebugInfo) FMLRelaunchLog.finer("Optional on %s triggered - mod missing %s", name, modId); if (optional.getAnnotationInfo().containsKey("iface")) { Boolean stripRefs = (Boolean)optional.getAnnotationInfo().get("striprefs"); if (stripRefs == null) stripRefs = Boolean.FALSE; stripInterface(classNode,(String)optional.getAnnotationInfo().get("iface"), stripRefs); } else { stripMethod(classNode, optional.getObjectName()); } } if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - class %s processed", name); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); }
@Override public MethodVisitor visitMethod(int mAccess, final String mName, final String mDesc, String mSignature, String[] mExceptions) { final boolean warn = !(clsName.equals("net/minecraft/client/Minecraft") || clsName.equals("net/minecraft/server/dedicated/DedicatedServer") || clsName.equals("net/minecraft/server/dedicated/ServerHangWatchdog") || clsName.equals("net/minecraft/server/dedicated/ServerHangWatchdog$1") || clsName.equals("net/minecraftforge/fml/common/FMLCommonHandler") || clsName.startsWith("com/jcraft/jogg/") || clsName.startsWith("scala/sys/") || clsName.startsWith("net/minecraft/server/gui/MinecraftServerGui") || clsName.startsWith("com/sun/jna/") ); return new MethodVisitor(Opcodes.ASM5, super.visitMethod(mAccess, mName, mDesc, mSignature, mExceptions)) { @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean isIntf) { if (opcode == Opcodes.INVOKESTATIC && owner.equals("java/lang/System") && name.equals("exit") && desc.equals("(I)V")) { if (warn) { FMLRelaunchLog.warning("============================================================="); FMLRelaunchLog.warning("MOD HAS DIRECT REFERENCE System.exit() THIS IS NOT ALLOWED REROUTING TO FML!"); FMLRelaunchLog.warning("Offender: %s.%s%s", ExitVisitor.this.clsName, mName, mDesc); FMLRelaunchLog.warning("Use FMLCommonHandler.exitJava instead"); FMLRelaunchLog.warning("============================================================="); } owner = ExitVisitor.callbackOwner; name = "systemExitCalled"; } else if (opcode == Opcodes.INVOKEVIRTUAL && owner.equals("java/lang/Runtime") && name.equals("exit") && desc.equals("(I)V")) { if (warn) { FMLRelaunchLog.warning("============================================================="); FMLRelaunchLog.warning("MOD HAS DIRECT REFERENCE Runtime.exit() THIS IS NOT ALLOWED REROUTING TO FML!"); FMLRelaunchLog.warning("Offender: %s.%s%s", ExitVisitor.this.clsName, mName, mDesc); FMLRelaunchLog.warning("Use FMLCommonHandler.exitJava instead"); FMLRelaunchLog.warning("============================================================="); } opcode = Opcodes.INVOKESTATIC; owner = ExitVisitor.callbackOwner; name = "runtimeExitCalled"; desc = "(Ljava/lang/Runtime;I)V"; } else if (opcode == Opcodes.INVOKEVIRTUAL && owner.equals("java/lang/Runtime") && name.equals("halt") && desc.equals("(I)V")) { if (warn) { FMLRelaunchLog.warning("============================================================="); FMLRelaunchLog.warning("MOD HAS DIRECT REFERENCE Runtime.halt() THIS IS NOT ALLOWED REROUTING TO FML!"); FMLRelaunchLog.warning("Offendor: %s.%s%s", ExitVisitor.this.clsName, mName, mDesc); FMLRelaunchLog.warning("Use FMLCommonHandler.exitJava instead"); FMLRelaunchLog.warning("============================================================="); } opcode = Opcodes.INVOKESTATIC; owner = ExitVisitor.callbackOwner; name = "runtimeHaltCalled"; desc = "(Ljava/lang/Runtime;I)V"; } super.visitMethodInsn(opcode, owner, name, desc, isIntf); } }; }
public static void info(String message) { FMLRelaunchLog.log(GlobalNames.Domain, Level.INFO, message); }
public static void severe(String message) { FMLRelaunchLog.log(GlobalNames.Domain, Level.ERROR, message); }
public static void debug(String message) { FMLRelaunchLog.log(GlobalNames.Domain, Level.INFO, "Debug: " + message); }
public static void exception(Exception e) { FMLRelaunchLog.log(GlobalNames.Domain, Level.ERROR, e.getMessage()); }
void log(Object msg) { FMLRelaunchLog.info(msg.toString()); }
private static void log(Level level, String format, Object... data) { FMLRelaunchLog.log(Constants.name, level, format, data); }