From b280104d2f926ab74772cef2bf1602663cefa312 Mon Sep 17 00:00:00 2001 From: Thog Date: Tue, 16 May 2017 00:24:29 +0200 Subject: Remove the converter + some reorganization --- src/main/java/cuchaz/enigma/ConvertMain.java | 357 ----------- .../java/cuchaz/enigma/TranslatingTypeLoader.java | 19 +- .../java/cuchaz/enigma/analysis/BridgeMarker.java | 10 +- .../cuchaz/enigma/bytecode/ClassTranslator.java | 165 ----- .../cuchaz/enigma/bytecode/InnerClassWriter.java | 151 ----- .../enigma/bytecode/LocalVariableRenamer.java | 150 ----- .../enigma/bytecode/MethodParameterWriter.java | 67 -- .../bytecode/translators/ClassTranslator.java | 161 +++++ .../bytecode/translators/InnerClassWriter.java | 144 +++++ .../translators/LocalVariableTranslator.java | 142 ++++ .../translators/MethodParameterTranslator.java | 62 ++ .../java/cuchaz/enigma/convert/ClassForest.java | 59 -- .../cuchaz/enigma/convert/ClassIdentifier.java | 54 -- .../java/cuchaz/enigma/convert/ClassIdentity.java | 439 ------------- .../java/cuchaz/enigma/convert/ClassMatch.java | 83 --- .../java/cuchaz/enigma/convert/ClassMatches.java | 158 ----- .../java/cuchaz/enigma/convert/ClassMatching.java | 153 ----- .../java/cuchaz/enigma/convert/ClassNamer.java | 56 -- .../java/cuchaz/enigma/convert/FieldMatches.java | 150 ----- .../cuchaz/enigma/convert/MappingsConverter.java | 711 --------------------- .../java/cuchaz/enigma/convert/MatchesReader.java | 105 --- .../java/cuchaz/enigma/convert/MatchesWriter.java | 123 ---- .../java/cuchaz/enigma/convert/MemberMatches.java | 179 ------ .../java/cuchaz/enigma/gui/ClassMatchingGui.java | 536 ---------------- .../java/cuchaz/enigma/gui/MemberMatchingGui.java | 435 ------------- 25 files changed, 521 insertions(+), 4148 deletions(-) delete mode 100644 src/main/java/cuchaz/enigma/ConvertMain.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java delete mode 100644 src/main/java/cuchaz/enigma/convert/ClassForest.java delete mode 100644 src/main/java/cuchaz/enigma/convert/ClassIdentifier.java delete mode 100644 src/main/java/cuchaz/enigma/convert/ClassIdentity.java delete mode 100644 src/main/java/cuchaz/enigma/convert/ClassMatch.java delete mode 100644 src/main/java/cuchaz/enigma/convert/ClassMatches.java delete mode 100644 src/main/java/cuchaz/enigma/convert/ClassMatching.java delete mode 100644 src/main/java/cuchaz/enigma/convert/ClassNamer.java delete mode 100644 src/main/java/cuchaz/enigma/convert/FieldMatches.java delete mode 100644 src/main/java/cuchaz/enigma/convert/MappingsConverter.java delete mode 100644 src/main/java/cuchaz/enigma/convert/MatchesReader.java delete mode 100644 src/main/java/cuchaz/enigma/convert/MatchesWriter.java delete mode 100644 src/main/java/cuchaz/enigma/convert/MemberMatches.java delete mode 100644 src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java delete mode 100644 src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java (limited to 'src') diff --git a/src/main/java/cuchaz/enigma/ConvertMain.java b/src/main/java/cuchaz/enigma/ConvertMain.java deleted file mode 100644 index 3d58f57..0000000 --- a/src/main/java/cuchaz/enigma/ConvertMain.java +++ /dev/null @@ -1,357 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma; - -import cuchaz.enigma.convert.*; -import cuchaz.enigma.gui.ClassMatchingGui; -import cuchaz.enigma.gui.MemberMatchingGui; -import cuchaz.enigma.mapping.*; -import cuchaz.enigma.throwables.MappingConflict; -import cuchaz.enigma.throwables.MappingParseException; - -import java.io.File; -import java.io.IOException; -import java.util.jar.JarFile; - -public class ConvertMain { - - public static void main(String[] args) - throws IOException, MappingParseException { - try { - //Get all are args - String JarOld = getArg(args, 1, "Path to Old Jar", true); - String JarNew = getArg(args, 2, "Path to New Jar", true); - String OldMappings = getArg(args, 3, "Path to old .mappings file", true); - String NewMappings = getArg(args, 4, "Path to new .mappings file", true); - String ClassMatches = getArg(args, 5, "Path to Class .matches file", true); - String FieldMatches = getArg(args, 6, "Path to Field .matches file", true); - String MethodMatches = getArg(args, 7, "Path to Method .matches file", true); - //OldJar - JarFile sourceJar = new JarFile(new File(JarOld)); - //NewJar - JarFile destJar = new JarFile(new File(JarNew)); - //Get the mapping files - File inMappingsFile = new File(OldMappings); - File outMappingsFile = new File(NewMappings); - Mappings mappings = new MappingsEnigmaReader().read(inMappingsFile); - //Make the Match Files.. - File classMatchesFile = new File(ClassMatches); - File fieldMatchesFile = new File(FieldMatches); - File methodMatchesFile = new File(MethodMatches); - - String command = getArg(args, 0, "command", true); - - if (command.equalsIgnoreCase("computeClassMatches")) { - computeClassMatches(classMatchesFile, sourceJar, destJar, mappings); - convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); - } else if (command.equalsIgnoreCase("editClassMatches")) { - editClasssMatches(classMatchesFile, sourceJar, destJar, mappings); - convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); - } else if (command.equalsIgnoreCase("computeFieldMatches")) { - computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); - convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); - } else if (command.equalsIgnoreCase("editFieldMatches")) { - editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); - convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); - } else if (command.equalsIgnoreCase("computeMethodMatches")) { - computeMethodMatches(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); - convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); - } else if (command.equalsIgnoreCase("editMethodMatches")) { - editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile); - convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); - } else if (command.equalsIgnoreCase("convertMappings")) { - convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); - } - } catch (MappingConflict ex) { - System.out.println(ex.getMessage()); - ex.printStackTrace(); - } catch (IllegalArgumentException ex) { - System.out.println(ex.getMessage()); - printHelp(); - } - } - - private static void printHelp() { - System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION)); - System.out.println("Usage:"); - System.out.println("\tjava -cp enigma.jar cuchaz.enigma.ConvertMain "); - System.out.println("\tWhere is one of:"); - System.out.println("\t\tcomputeClassMatches"); - System.out.println("\t\teditClassMatches"); - System.out.println("\t\tcomputeFieldMatches"); - System.out.println("\t\teditFieldMatches"); - System.out.println("\t\teditMethodMatches"); - System.out.println("\t\tconvertMappings"); - System.out.println("\tWhere is the already mapped jar."); - System.out.println("\tWhere is the unmapped jar."); - System.out.println("\tWhere is the path to the mappings for the old jar."); - System.out.println("\tWhere is the new mappings. (Where you want to save them and there name)"); - System.out.println("\tWhere is the class matches file."); - System.out.println("\tWhere is the field matches file."); - System.out.println("\tWhere is the method matches file."); - } - - //Copy of getArg from CommandMain.... Should make a utils class. - private static String getArg(String[] args, int i, String name, boolean required) { - if (i >= args.length) { - if (required) { - throw new IllegalArgumentException(name + " is required"); - } else { - return null; - } - } - return args[i]; - } - - private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) - throws IOException { - ClassMatches classMatches = MappingsConverter.computeClassMatches(sourceJar, destJar, mappings); - MatchesWriter.writeClasses(classMatches, classMatchesFile); - System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath()); - } - - private static void editClasssMatches(final File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) - throws IOException { - System.out.println("Reading class matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); - deobfuscators.source.setMappings(mappings); - System.out.println("Starting GUI..."); - new ClassMatchingGui(classMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(matches -> - { - try { - MatchesWriter.writeClasses(matches, classMatchesFile); - } catch (IOException ex) { - throw new Error(ex); - } - }); - } - - @SuppressWarnings("unused") - private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile) - throws IOException, MappingConflict { - System.out.println("Reading class matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); - deobfuscators.source.setMappings(mappings); - - Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); - new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); - System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); - } - - private static void computeFieldMatches(File memberMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) - throws IOException, MappingParseException { - - System.out.println("Reading class matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - System.out.println("Reading mappings..."); - Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); - System.out.println("Indexing dest jar..."); - Deobfuscator destDeobfuscator = new Deobfuscator(destJar); - - System.out.println("Writing matches..."); - - // get the matched and unmatched mappings - MemberMatches fieldMatches = MappingsConverter.computeMemberMatches( - destDeobfuscator, - destMappings, - classMatches, - MappingsConverter.getFieldDoer() - ); - - MatchesWriter.writeMembers(fieldMatches, memberMatchesFile); - System.out.println("Wrote:\n\t" + memberMatchesFile.getAbsolutePath()); - } - - private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) - throws IOException, MappingParseException { - - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - MemberMatches fieldMatches = MatchesReader.readMembers(fieldMatchesFile); - - // prep deobfuscators - Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); - deobfuscators.source.setMappings(sourceMappings); - Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); - MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); - checker.dropBrokenMappings(destMappings); - deobfuscators.dest.setMappings(destMappings); - - new MemberMatchingGui<>(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener( - matches -> - { - try { - MatchesWriter.writeMembers(matches, fieldMatchesFile); - } catch (IOException ex) { - throw new Error(ex); - } - }); - } - - @SuppressWarnings("unused") - private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile) - throws IOException, MappingConflict { - - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - MemberMatches fieldMatches = MatchesReader.readMembers(fieldMatchesFile); - - Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); - deobfuscators.source.setMappings(mappings); - - // apply matches - Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); - MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); - - // write out the converted mappings - - new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); - System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); - } - - private static void computeMethodMatches(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings sourceMappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) - throws IOException, MappingParseException { - - System.out.println("Reading class matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - System.out.println("Reading dest mappings..."); - Mappings destMappings = new MappingsEnigmaReader().read(outMappingsFile); - System.out.println("Indexing dest jar..."); - Deobfuscator destDeobfuscator = new Deobfuscator(destJar); - System.out.println("Indexing source jar..."); - Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); - - System.out.println("Writing method matches..."); - - // get the matched and unmatched mappings - MemberMatches methodMatches = MappingsConverter.computeMethodsMatches( - destDeobfuscator, - destMappings, - sourceDeobfuscator, - sourceMappings, - classMatches, - MappingsConverter.getMethodDoer() - ); - - MatchesWriter.writeMembers(methodMatches, methodMatchesFile); - System.out.println("Wrote:\n\t" + methodMatchesFile.getAbsolutePath()); - } - - private static void editMethodMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File methodMatchesFile) - throws IOException, MappingParseException { - - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - MemberMatches methodMatches = MatchesReader.readMembers(methodMatchesFile); - - // prep deobfuscators - Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); - deobfuscators.source.setMappings(sourceMappings); - Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); - MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); - checker.dropBrokenMappings(destMappings); - deobfuscators.dest.setMappings(destMappings); - - new MemberMatchingGui<>(classMatches, methodMatches, deobfuscators.source, deobfuscators.dest).setSaveListener( - matches -> - { - try { - MatchesWriter.writeMembers(matches, methodMatchesFile); - } catch (IOException ex) { - throw new Error(ex); - } - }); - } - - private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) - throws IOException, MappingConflict { - - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); - MemberMatches fieldMatches = MatchesReader.readMembers(fieldMatchesFile); - MemberMatches methodMatches = MatchesReader.readMembers(methodMatchesFile); - - Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); - deobfuscators.source.setMappings(mappings); - - // apply matches - Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); - MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); - MappingsConverter.applyMemberMatches(newMappings, classMatches, methodMatches, MappingsConverter.getMethodDoer()); - - // check the final mappings - MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); - checker.dropBrokenMappings(newMappings); - - for (java.util.Map.Entry mapping : checker.getDroppedClassMappings().entrySet()) { - System.out.println("WARNING: Broken class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); - } - for (java.util.Map.Entry mapping : checker.getDroppedInnerClassMappings().entrySet()) { - System.out.println("WARNING: Broken inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); - } - for (java.util.Map.Entry mapping : checker.getDroppedFieldMappings().entrySet()) { - System.out.println("WARNING: Broken field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); - } - for (java.util.Map.Entry mapping : checker.getDroppedMethodMappings().entrySet()) { - System.out.println("WARNING: Broken behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); - } - - // write out the converted mappings - new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); - System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); - } - - private static class Deobfuscators { - - public Deobfuscator source; - public Deobfuscator dest; - - public Deobfuscators(JarFile sourceJar, JarFile destJar) { - System.out.println("Indexing source jar..."); - IndexerThread sourceIndexer = new IndexerThread(sourceJar); - sourceIndexer.start(); - System.out.println("Indexing dest jar..."); - IndexerThread destIndexer = new IndexerThread(destJar); - destIndexer.start(); - sourceIndexer.joinOrBail(); - destIndexer.joinOrBail(); - source = sourceIndexer.deobfuscator; - dest = destIndexer.deobfuscator; - } - } - - private static class IndexerThread extends Thread { - - public Deobfuscator deobfuscator; - private JarFile jarFile; - - public IndexerThread(JarFile jarFile) { - this.jarFile = jarFile; - deobfuscator = null; - } - - public void joinOrBail() { - try { - join(); - } catch (InterruptedException ex) { - throw new Error(ex); - } - } - - @Override - public void run() { - deobfuscator = new Deobfuscator(jarFile); - } - } -} \ No newline at end of file diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java index 7304f72..2a2041a 100644 --- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java @@ -18,10 +18,10 @@ import com.strobel.assembler.metadata.ClasspathTypeLoader; import com.strobel.assembler.metadata.ITypeLoader; import cuchaz.enigma.analysis.BridgeMarker; import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.bytecode.ClassTranslator; -import cuchaz.enigma.bytecode.InnerClassWriter; -import cuchaz.enigma.bytecode.LocalVariableRenamer; -import cuchaz.enigma.bytecode.MethodParameterWriter; +import cuchaz.enigma.bytecode.translators.ClassTranslator; +import cuchaz.enigma.bytecode.translators.InnerClassWriter; +import cuchaz.enigma.bytecode.translators.LocalVariableTranslator; +import cuchaz.enigma.bytecode.translators.MethodParameterTranslator; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Translator; import javassist.*; @@ -51,6 +51,7 @@ public class TranslatingTypeLoader implements ITypeLoader { this.deobfuscatingTranslator = deobfuscatingTranslator; this.cache = Maps.newHashMap(); this.defaultTypeLoader = new ClasspathTypeLoader(); + } public void clearCache() { @@ -200,7 +201,7 @@ public class TranslatingTypeLoader implements ITypeLoader { throws IOException, NotFoundException, CannotCompileException { // reconstruct inner classes - new InnerClassWriter(this.jarIndex, this.deobfuscatingTranslator).write(c); + InnerClassWriter.write(jarIndex, c); // re-get the javassist handle since we changed class names ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); @@ -213,10 +214,10 @@ public class TranslatingTypeLoader implements ITypeLoader { assertClassName(c, obfClassEntry); // do all kinds of deobfuscating transformations on the class - new BridgeMarker(this.jarIndex).markBridges(c); - new MethodParameterWriter(this.deobfuscatingTranslator).writeMethodArguments(c); - new LocalVariableRenamer(this.deobfuscatingTranslator).rename(c); - new ClassTranslator(this.deobfuscatingTranslator).translate(c); + BridgeMarker.markBridges(this.jarIndex, c); + MethodParameterTranslator.translate(this.deobfuscatingTranslator, c); + LocalVariableTranslator.translate(this.deobfuscatingTranslator, c); + ClassTranslator.translate(this.deobfuscatingTranslator, c); return c; } diff --git a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java index 81e750c..a2f1f90 100644 --- a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java +++ b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java @@ -19,19 +19,13 @@ import javassist.bytecode.AccessFlag; public class BridgeMarker { - private JarIndex jarIndex; - - public BridgeMarker(JarIndex jarIndex) { - this.jarIndex = jarIndex; - } - - public void markBridges(CtClass c) { + public static void markBridges(JarIndex jarIndex, CtClass c) { for (CtMethod method : c.getDeclaredMethods()) { MethodEntry methodEntry = EntryFactory.getMethodEntry(method); // is this a bridge method? - MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry); + MethodEntry bridgedMethodEntry = jarIndex.getBridgedMethod(methodEntry); if (bridgedMethodEntry != null) { // it's a bridge method! add the bridge flag diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java deleted file mode 100644 index 1ebf656..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java +++ /dev/null @@ -1,165 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtField; -import javassist.CtMethod; -import javassist.bytecode.*; - -public class ClassTranslator { - - private Translator translator; - - public ClassTranslator(Translator translator) { - this.translator = translator; - } - - public void translate(CtClass c) { - - // NOTE: the order of these translations is very important - - // translate all the field and method references in the code by editing the constant pool - ConstPool constants = c.getClassFile().getConstPool(); - ConstPoolEditor editor = new ConstPoolEditor(constants); - for (int i = 1; i < constants.getSize(); i++) { - switch (constants.getTag(i)) { - - case ConstPool.CONST_Fieldref: { - - // translate the name and type - FieldEntry entry = EntryFactory.getFieldEntry( - Descriptor.toJvmName(constants.getFieldrefClassName(i)), - constants.getFieldrefName(i), - constants.getFieldrefType(i) - ); - FieldEntry translatedEntry = this.translator.translateEntry(entry); - if (!entry.equals(translatedEntry)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); - } - } - break; - - case ConstPool.CONST_Methodref: - case ConstPool.CONST_InterfaceMethodref: { - - // translate the name and type (ie signature) - BehaviorEntry entry = EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(editor.getMemberrefClassname(i)), - editor.getMemberrefName(i), - editor.getMemberrefType(i) - ); - BehaviorEntry translatedEntry = this.translator.translateEntry(entry); - if (!entry.equals(translatedEntry)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); - } - } - break; - default: - break; - } - } - - ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); - Mappings.EntryModifier modifier = this.translator.getModifier(classEntry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(c, modifier); - - // translate all the fields - for (CtField field : c.getDeclaredFields()) { - - // translate the name - FieldEntry entry = EntryFactory.getFieldEntry(field); - String translatedName = this.translator.translate(entry); - modifier = this.translator.getModifier(entry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(field, modifier); - - if (translatedName != null) { - field.setName(translatedName); - } - - // translate the type - Type translatedType = this.translator.translateType(entry.getType()); - field.getFieldInfo().setDescriptor(translatedType.toString()); - } - - // translate all the methods and constructors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); - - modifier = this.translator.getModifier(entry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(behavior, modifier); - - if (behavior instanceof CtMethod) { - CtMethod method = (CtMethod) behavior; - - // translate the name - String translatedName = this.translator.translate(entry); - if (translatedName != null) { - method.setName(translatedName); - } - } - - if (entry.getSignature() != null) { - // translate the signature - Signature translatedSignature = this.translator.translateSignature(entry.getSignature()); - behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); - } - } - - // translate the EnclosingMethod attribute - EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); - if (enclosingMethodAttr != null) { - - if (enclosingMethodAttr.methodIndex() == 0) { - BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); - BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); - c.getClassFile().addAttribute(new EnclosingMethodAttribute( - constants, - deobfBehaviorEntry.getClassName() - )); - } else { - BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(enclosingMethodAttr.className()), - enclosingMethodAttr.methodName(), - enclosingMethodAttr.methodDescriptor() - ); - BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); - c.getClassFile().addAttribute(new EnclosingMethodAttribute( - constants, - deobfBehaviorEntry.getClassName(), - deobfBehaviorEntry.getName(), - deobfBehaviorEntry.getSignature().toString() - )); - } - } - - // translate all the class names referenced in the code - // the above code only changed method/field/reference names and types, but not the rest of the class references - ClassRenamer.renameClasses(c, this.translator); - - // translate the source file attribute too - ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry); - if (deobfClassEntry != null) { - String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; - c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); - } - InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (attr != null) - InnerClassWriter.changeModifier(c, attr, translator); - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java deleted file mode 100644 index f1c3dd7..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java +++ /dev/null @@ -1,151 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import com.google.common.collect.Lists; -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.mapping.*; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.NotFoundException; -import javassist.bytecode.*; - -import java.util.Collection; -import java.util.List; - -public class InnerClassWriter { - - private JarIndex index; - private Translator deobfuscatorTranslator; - - public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) { - this.index = index; - this.deobfuscatorTranslator = deobfuscatorTranslator; - } - - // FIXME: modifier is not applied to inner class - public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) { - ClassPool pool = c.getClassPool(); - for (int i = 0; i < attr.tableLength(); i++) { - - String innerName = attr.innerClass(i); - // get the inner class full name (which has already been translated) - ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); - try { - CtClass innerClass = pool.get(innerName); - Mappings.EntryModifier modifier = translator.getModifier(classEntry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(innerClass, modifier); - } catch (NotFoundException e) { - // This shouldn't be possible in theory - //e.printStackTrace(); - } - } - } - - public void write(CtClass c) { - - // don't change anything if there's already an attribute there - InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (oldAttr != null) { - // bail! - return; - } - - ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); - List obfClassChain = this.index.getObfClassChain(obfClassEntry); - - boolean isInnerClass = obfClassChain.size() > 1; - if (isInnerClass) { - - // it's an inner class, rename it to the fully qualified name - c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); - - BehaviorEntry caller = this.index.getAnonymousClassCaller(obfClassEntry); - if (caller != null) { - - // write the enclosing method attribute - if (caller.getName().equals("")) { - c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); - } else { - c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); - } - } - } - - // does this class have any inner classes? - Collection obfInnerClassEntries = this.index.getInnerClasses(obfClassEntry); - - if (isInnerClass || !obfInnerClassEntries.isEmpty()) { - - // create an inner class attribute - InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); - c.getClassFile().addAttribute(attr); - - // write the ancestry, but not the outermost class - for (int i = 1; i < obfClassChain.size(); i++) { - ClassEntry obfInnerClassEntry = obfClassChain.get(i); - writeInnerClass(attr, obfClassChain, obfInnerClassEntry); - - // update references to use the fully qualified inner class name - c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); - } - - // write the inner classes - for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { - - // extend the class chain - List extendedObfClassChain = Lists.newArrayList(obfClassChain); - extendedObfClassChain.add(obfInnerClassEntry); - - writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); - - // update references to use the fully qualified inner class name - c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); - } - } - } - - private void writeInnerClass(InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { - - // get the new inner class name - ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); - ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); - - // here's what the JVM spec says about the InnerClasses attribute - // append(inner, parent, 0 if anonymous else simple name, flags); - - // update the attribute with this inner class - ConstPool constPool = attr.getConstPool(); - int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); - int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); - int innerClassNameIndex = 0; - int accessFlags = AccessFlag.PUBLIC; - // TODO: need to figure out if we can put static or not - if (!this.index.isAnonymousClass(obfClassEntry)) { - innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); - } - - attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); - - /* DEBUG - System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", - obfClassEntry, - attr.innerClass(attr.tableLength() - 1), - attr.outerClass(attr.tableLength() - 1), - attr.innerName(attr.tableLength() - 1), - Constants.NonePackage + "/" + obfInnerClassName, - obfClassEntry.getName() - )); - */ - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java deleted file mode 100644 index 878e30a..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java +++ /dev/null @@ -1,150 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.bytecode.*; - -public class LocalVariableRenamer { - - private Translator translator; - - public LocalVariableRenamer(Translator translator) { - this.translator = translator; - } - - public void rename(CtClass c) { - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - // if there's a local variable table, just rename everything to v1, v2, v3, ... for now - CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); - if (codeAttribute == null) { - continue; - } - - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - ConstPool constants = c.getClassFile().getConstPool(); - - LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); - if (table != null) { - renameLVT(behaviorEntry, constants, table, c); - } - - LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); - if (typeTable != null) { - renameLVTT(typeTable, table); - } - } - } - - // DEBUG - @SuppressWarnings("unused") - private void dumpTable(LocalVariableAttribute table) { - for (int i = 0; i < table.tableLength(); i++) { - System.out.println(String.format("\t%d (%d): %s %s", - i, table.index(i), table.variableName(i), table.descriptor(i) - )); - } - } - - private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) { - - // skip empty tables - if (table.tableLength() <= 0) { - return; - } - - // where do we start counting variables? - int starti = 0; - if (table.variableName(0).equals("this")) { - // skip the "this" variable - starti++; - } - - // rename method arguments first - int numArgs = 0; - if (behaviorEntry.getSignature() != null) { - numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); - boolean isNestedClassConstructor = false; - - // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! - if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) { - // Get the first arg type - Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); - - // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class - if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { - isNestedClassConstructor = true; - numArgs--; - } - } - - for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { - int argi = i - starti; - if (ctClass.isEnum()) - argi += 2; - if (behaviorEntry.getClassEntry().getName().contains("ahd") && behaviorEntry instanceof ConstructorEntry) - System.out.println(behaviorEntry.getClassEntry() + " " + i); - String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); - if (argName == null) { - int argIndex = isNestedClassConstructor ? argi + 1 : argi; - if (ctClass.isEnum()) - argIndex -= 2; - Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex); - // Unfortunately each of these have different name getters, so they have different code paths - if (argType.isPrimitive()) { - Type.Primitive argCls = argType.getPrimitive(); - argName = "a" + argCls.name() + (argIndex + 1); - } else if (argType.isArray()) { - // List types would require this whole block again, so just go with aListx - argName = "aList" + (argIndex + 1); - } else if (argType.isClass()) { - ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry()); - argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1); - } else { - argName = "a" + (argIndex + 1); - } - } - renameVariable(table, i, constants.addUtf8Info(argName)); - } - } - - // then rename the rest of the args, if any - for (int i = starti + numArgs; i < table.tableLength(); i++) { - int firstIndex = Math.min(table.index(starti + numArgs), table.index(i)); - renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); - } - } - - private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { - // rename args to the same names as in the LVT - for (int i = 0; i < typeTable.tableLength(); i++) { - renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); - } - } - - private void renameVariable(LocalVariableAttribute table, int i, int stringId) { - // based off of LocalVariableAttribute.nameIndex() - ByteArray.write16bit(stringId, table.get(), i * 10 + 6); - } - - private int getNameIndex(LocalVariableAttribute table, int index) { - for (int i = 0; i < table.tableLength(); i++) { - if (table.index(i) == index) { - return table.nameIndex(i); - } - } - return 0; - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java deleted file mode 100644 index d63572e..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.bytecode.CodeAttribute; -import javassist.bytecode.LocalVariableAttribute; - -import java.util.ArrayList; -import java.util.List; - -public class MethodParameterWriter { - - private Translator translator; - - public MethodParameterWriter(Translator translator) { - this.translator = translator; - } - - public void writeMethodArguments(CtClass c) { - - // Procyon will read method arguments from the "MethodParameters" attribute, so write those - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - // if there's a local variable table here, don't write a MethodParameters attribute - // let the local variable writer deal with it instead - // procyon starts doing really weird things if we give it both attributes - CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); - if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { - continue; - } - - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - - // get the number of arguments - Signature signature = behaviorEntry.getSignature(); - if (signature == null) { - // static initializers have no signatures, or arguments - continue; - } - int numParams = signature.getArgumentTypes().size(); - if (numParams <= 0) { - continue; - } - - // get the list of argument names - List names = new ArrayList<>(numParams); - for (int i = 0; i < numParams; i++) { - names.add(this.translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); - } - - // save the mappings to the class - MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java new file mode 100644 index 0000000..4ac5a8b --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.bytecode.translators; + +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.bytecode.ConstPoolEditor; +import cuchaz.enigma.mapping.*; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.*; + +public class ClassTranslator { + + public static void translate(Translator translator, CtClass c) { + + // NOTE: the order of these translations is very important + + // translate all the field and method references in the code by editing the constant pool + ConstPool constants = c.getClassFile().getConstPool(); + ConstPoolEditor editor = new ConstPoolEditor(constants); + for (int i = 1; i < constants.getSize(); i++) { + switch (constants.getTag(i)) { + + case ConstPool.CONST_Fieldref: { + + // translate the name and type + FieldEntry entry = EntryFactory.getFieldEntry( + Descriptor.toJvmName(constants.getFieldrefClassName(i)), + constants.getFieldrefName(i), + constants.getFieldrefType(i) + ); + FieldEntry translatedEntry = translator.translateEntry(entry); + if (!entry.equals(translatedEntry)) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); + } + } + break; + + case ConstPool.CONST_Methodref: + case ConstPool.CONST_InterfaceMethodref: { + + // translate the name and type (ie signature) + BehaviorEntry entry = EntryFactory.getBehaviorEntry( + Descriptor.toJvmName(editor.getMemberrefClassname(i)), + editor.getMemberrefName(i), + editor.getMemberrefType(i) + ); + BehaviorEntry translatedEntry = translator.translateEntry(entry); + if (!entry.equals(translatedEntry)) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); + } + } + break; + default: + break; + } + } + + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + Mappings.EntryModifier modifier = translator.getModifier(classEntry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(c, modifier); + + // translate all the fields + for (CtField field : c.getDeclaredFields()) { + + // translate the name + FieldEntry entry = EntryFactory.getFieldEntry(field); + String translatedName = translator.translate(entry); + modifier = translator.getModifier(entry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(field, modifier); + + if (translatedName != null) { + field.setName(translatedName); + } + + // translate the type + Type translatedType = translator.translateType(entry.getType()); + field.getFieldInfo().setDescriptor(translatedType.toString()); + } + + // translate all the methods and constructors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + + BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); + + modifier = translator.getModifier(entry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(behavior, modifier); + + if (behavior instanceof CtMethod) { + CtMethod method = (CtMethod) behavior; + + // translate the name + String translatedName = translator.translate(entry); + if (translatedName != null) { + method.setName(translatedName); + } + } + + if (entry.getSignature() != null) { + // translate the signature + Signature translatedSignature = translator.translateSignature(entry.getSignature()); + behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); + } + } + + // translate the EnclosingMethod attribute + EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); + if (enclosingMethodAttr != null) { + + if (enclosingMethodAttr.methodIndex() == 0) { + BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); + BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry); + c.getClassFile().addAttribute(new EnclosingMethodAttribute( + constants, + deobfBehaviorEntry.getClassName() + )); + } else { + BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( + Descriptor.toJvmName(enclosingMethodAttr.className()), + enclosingMethodAttr.methodName(), + enclosingMethodAttr.methodDescriptor() + ); + BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry); + c.getClassFile().addAttribute(new EnclosingMethodAttribute( + constants, + deobfBehaviorEntry.getClassName(), + deobfBehaviorEntry.getName(), + deobfBehaviorEntry.getSignature().toString() + )); + } + } + + // translate all the class names referenced in the code + // the above code only changed method/field/reference names and types, but not the rest of the class references + ClassRenamer.renameClasses(c, translator); + + // translate the source file attribute too + ClassEntry deobfClassEntry = translator.translateEntry(classEntry); + if (deobfClassEntry != null) { + String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; + c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); + } + InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (attr != null) + InnerClassWriter.changeModifier(c, attr, translator); + } +} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java new file mode 100644 index 0000000..0e35938 --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.bytecode.translators; + +import com.google.common.collect.Lists; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.mapping.*; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.*; + +import java.util.Collection; +import java.util.List; + +public class InnerClassWriter { + + // FIXME: modifier is not applied to inner class + public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) { + ClassPool pool = c.getClassPool(); + for (int i = 0; i < attr.tableLength(); i++) { + + String innerName = attr.innerClass(i); + // get the inner class full name (which has already been translated) + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); + try { + CtClass innerClass = pool.get(innerName); + Mappings.EntryModifier modifier = translator.getModifier(classEntry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(innerClass, modifier); + } catch (NotFoundException e) { + // This shouldn't be possible in theory + //e.printStackTrace(); + } + } + } + + public static void write(JarIndex index, CtClass c) { + + // don't change anything if there's already an attribute there + InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (oldAttr != null) { + // bail! + return; + } + + ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); + List obfClassChain = index.getObfClassChain(obfClassEntry); + + boolean isInnerClass = obfClassChain.size() > 1; + if (isInnerClass) { + + // it's an inner class, rename it to the fully qualified name + c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); + + BehaviorEntry caller = index.getAnonymousClassCaller(obfClassEntry); + if (caller != null) { + + // write the enclosing method attribute + if (caller.getName().equals("")) { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); + } else { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); + } + } + } + + // does this class have any inner classes? + Collection obfInnerClassEntries = index.getInnerClasses(obfClassEntry); + + if (isInnerClass || !obfInnerClassEntries.isEmpty()) { + + // create an inner class attribute + InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); + c.getClassFile().addAttribute(attr); + + // write the ancestry, but not the outermost class + for (int i = 1; i < obfClassChain.size(); i++) { + ClassEntry obfInnerClassEntry = obfClassChain.get(i); + writeInnerClass(index, attr, obfClassChain, obfInnerClassEntry); + + // update references to use the fully qualified inner class name + c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); + } + + // write the inner classes + for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { + + // extend the class chain + List extendedObfClassChain = Lists.newArrayList(obfClassChain); + extendedObfClassChain.add(obfInnerClassEntry); + + writeInnerClass(index, attr, extendedObfClassChain, obfInnerClassEntry); + + // update references to use the fully qualified inner class name + c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); + } + } + } + + private static void writeInnerClass(JarIndex index, InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { + + // get the new inner class name + ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); + ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); + + // here's what the JVM spec says about the InnerClasses attribute + // append(inner, parent, 0 if anonymous else simple name, flags); + + // update the attribute with this inner class + ConstPool constPool = attr.getConstPool(); + int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); + int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); + int innerClassNameIndex = 0; + int accessFlags = AccessFlag.PUBLIC; + // TODO: need to figure out if we can put static or not + if (!index.isAnonymousClass(obfClassEntry)) { + innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); + } + + attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); + + /* DEBUG + System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", + obfClassEntry, + attr.innerClass(attr.tableLength() - 1), + attr.outerClass(attr.tableLength() - 1), + attr.innerName(attr.tableLength() - 1), + Constants.NonePackage + "/" + obfInnerClassName, + obfClassEntry.getName() + )); + */ + } +} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java new file mode 100644 index 0000000..51b3d2d --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.bytecode.translators; + +import cuchaz.enigma.mapping.*; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.bytecode.*; + +public class LocalVariableTranslator { + + public static void translate(Translator translator, CtClass c) { + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + + // if there's a local variable table, just rename everything to v1, v2, v3, ... for now + CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); + if (codeAttribute == null) { + continue; + } + + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + ConstPool constants = c.getClassFile().getConstPool(); + + LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); + if (table != null) { + renameLVT(translator, behaviorEntry, constants, table, c); + } + + LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); + if (typeTable != null) { + renameLVTT(typeTable, table); + } + } + } + + // DEBUG + @SuppressWarnings("unused") + private static void dumpTable(LocalVariableAttribute table) { + for (int i = 0; i < table.tableLength(); i++) { + System.out.println(String.format("\t%d (%d): %s %s", + i, table.index(i), table.variableName(i), table.descriptor(i) + )); + } + } + + private static void renameLVT(Translator translator, BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) { + + // skip empty tables + if (table.tableLength() <= 0) { + return; + } + + // where do we start counting variables? + int starti = 0; + if (table.variableName(0).equals("this")) { + // skip the "this" variable + starti++; + } + + // rename method arguments first + int numArgs = 0; + if (behaviorEntry.getSignature() != null) { + numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); + boolean isNestedClassConstructor = false; + + // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! + if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) { + // Get the first arg type + Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); + + // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class + if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { + isNestedClassConstructor = true; + numArgs--; + } + } + + for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { + int argi = i - starti; + if (ctClass.isEnum()) + argi += 2; + String argName = translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); + if (argName == null) { + int argIndex = isNestedClassConstructor ? argi + 1 : argi; + if (ctClass.isEnum()) + argIndex -= 2; + Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex); + // Unfortunately each of these have different name getters, so they have different code paths + if (argType.isPrimitive()) { + Type.Primitive argCls = argType.getPrimitive(); + argName = "a" + argCls.name() + (argIndex + 1); + } else if (argType.isArray()) { + // List types would require this whole block again, so just go with aListx + argName = "aList" + (argIndex + 1); + } else if (argType.isClass()) { + ClassEntry argClsTrans = translator.translateEntry(argType.getClassEntry()); + argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1); + } else { + argName = "a" + (argIndex + 1); + } + } + renameVariable(table, i, constants.addUtf8Info(argName)); + } + } + + // then rename the rest of the args, if any + for (int i = starti + numArgs; i < table.tableLength(); i++) { + int firstIndex = Math.min(table.index(starti + numArgs), table.index(i)); + renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); + } + } + + private static void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { + // rename args to the same names as in the LVT + for (int i = 0; i < typeTable.tableLength(); i++) { + renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); + } + } + + private static void renameVariable(LocalVariableAttribute table, int i, int stringId) { + // based off of LocalVariableAttribute.nameIndex() + ByteArray.write16bit(stringId, table.get(), i * 10 + 6); + } + + private static int getNameIndex(LocalVariableAttribute table, int index) { + for (int i = 0; i < table.tableLength(); i++) { + if (table.index(i) == index) { + return table.nameIndex(i); + } + } + return 0; + } +} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java new file mode 100644 index 0000000..4e632b9 --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.bytecode.translators; + +import cuchaz.enigma.bytecode.MethodParametersAttribute; +import cuchaz.enigma.mapping.*; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.LocalVariableAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class MethodParameterTranslator { + + public static void translate(Translator translator, CtClass c) { + + // Procyon will read method arguments from the "MethodParameters" attribute, so write those + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + + // if there's a local variable table here, don't write a MethodParameters attribute + // let the local variable writer deal with it instead + // procyon starts doing really weird things if we give it both attributes + CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); + if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { + continue; + } + + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + + // get the number of arguments + Signature signature = behaviorEntry.getSignature(); + if (signature == null) { + // static initializers have no signatures, or arguments + continue; + } + int numParams = signature.getArgumentTypes().size(); + if (numParams <= 0) { + continue; + } + + // get the list of argument names + List names = new ArrayList<>(numParams); + for (int i = 0; i < numParams; i++) { + names.add(translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); + } + + // save the mappings to the class + MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); + } + } +} diff --git a/src/main/java/cuchaz/enigma/convert/ClassForest.java b/src/main/java/cuchaz/enigma/convert/ClassForest.java deleted file mode 100644 index 4542fb3..0000000 --- a/src/main/java/cuchaz/enigma/convert/ClassForest.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import cuchaz.enigma.mapping.ClassEntry; - -import java.util.Collection; - -public class ClassForest { - - private ClassIdentifier identifier; - private Multimap forest; - - public ClassForest(ClassIdentifier identifier) { - this.identifier = identifier; - this.forest = HashMultimap.create(); - } - - public void addAll(Iterable entries) { - for (ClassEntry entry : entries) { - add(entry); - } - } - - public void add(ClassEntry entry) { - try { - this.forest.put(this.identifier.identify(entry), entry); - } catch (ClassNotFoundException ex) { - throw new Error("Unable to find class " + entry.getName()); - } - } - - public Collection identities() { - return this.forest.keySet(); - } - - public Collection classes() { - return this.forest.values(); - } - - public Collection getClasses(ClassIdentity identity) { - return this.forest.get(identity); - } - - public boolean containsIdentity(ClassIdentity identity) { - return this.forest.containsKey(identity); - } -} diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java b/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java deleted file mode 100644 index 0a72073..0000000 --- a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.Maps; -import cuchaz.enigma.TranslatingTypeLoader; -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Translator; -import javassist.CtClass; - -import java.util.Map; -import java.util.jar.JarFile; - -public class ClassIdentifier { - - private JarIndex index; - private SidedClassNamer namer; - private boolean useReferences; - private TranslatingTypeLoader loader; - private Map cache; - - public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) { - this.index = index; - this.namer = namer; - this.useReferences = useReferences; - this.loader = new TranslatingTypeLoader(jar, index, new Translator(), new Translator()); - this.cache = Maps.newHashMap(); - } - - public ClassIdentity identify(ClassEntry classEntry) - throws ClassNotFoundException { - ClassIdentity identity = this.cache.get(classEntry); - if (identity == null) { - CtClass c = this.loader.loadClass(classEntry.getName()); - if (c == null) { - throw new ClassNotFoundException(classEntry.getName()); - } - identity = new ClassIdentity(c, this.namer, this.index, this.useReferences); - this.cache.put(classEntry, identity); - } - return identity; - } -} diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java deleted file mode 100644 index a395b75..0000000 --- a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java +++ /dev/null @@ -1,439 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.*; -import cuchaz.enigma.analysis.ClassImplementationsTreeNode; -import cuchaz.enigma.analysis.EntryReference; -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.bytecode.ConstPoolEditor; -import cuchaz.enigma.bytecode.InfoType; -import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; -import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; -import cuchaz.enigma.mapping.*; -import cuchaz.enigma.utils.Utils; -import javassist.*; -import javassist.bytecode.*; -import javassist.expr.*; - -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class ClassIdentity { - - private ClassEntry classEntry; - private SidedClassNamer namer; - private final ClassNameReplacer classNameReplacer = new ClassNameReplacer() { - - private Map classNames = Maps.newHashMap(); - - @Override - public String replace(String className) { - - // classes not in the none package can be passed through - ClassEntry classEntry = new ClassEntry(className); - if (classEntry.getPackageName() != null) { - return className; - } - - // is this class ourself? - if (className.equals(classEntry.getName())) { - return "CSelf"; - } - - // try the namer - if (namer != null) { - String newName = namer.getName(className); - if (newName != null) { - return newName; - } - } - - // otherwise, use local naming - if (!classNames.containsKey(className)) { - classNames.put(className, getNewClassName()); - } - return classNames.get(className); - } - - private String getNewClassName() { - return String.format("C%03d", classNames.size()); - } - }; - private Multiset fields; - private Multiset methods; - private Multiset constructors; - private String staticInitializer; - private String extendz; - private Multiset implementz; - private Set stringLiterals; - private Multiset implementations; - private Multiset references; - private String outer; - - public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { - this.namer = namer; - - // stuff from the bytecode - - this.classEntry = EntryFactory.getClassEntry(c); - this.fields = HashMultiset.create(); - for (CtField field : c.getDeclaredFields()) { - this.fields.add(scrubType(field.getSignature())); - } - this.methods = HashMultiset.create(); - for (CtMethod method : c.getDeclaredMethods()) { - this.methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); - } - this.constructors = HashMultiset.create(); - for (CtConstructor constructor : c.getDeclaredConstructors()) { - this.constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); - } - this.staticInitializer = ""; - if (c.getClassInitializer() != null) { - this.staticInitializer = getBehaviorSignature(c.getClassInitializer()); - } - this.extendz = ""; - if (c.getClassFile().getSuperclass() != null) { - this.extendz = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass())); - } - this.implementz = HashMultiset.create(); - for (String interfaceName : c.getClassFile().getInterfaces()) { - this.implementz.add(scrubClassName(Descriptor.toJvmName(interfaceName))); - } - - this.stringLiterals = Sets.newHashSet(); - ConstPool constants = c.getClassFile().getConstPool(); - for (int i = 1; i < constants.getSize(); i++) { - if (constants.getTag(i) == ConstPool.CONST_String) { - this.stringLiterals.add(constants.getStringInfo(i)); - } - } - - // stuff from the jar index - - this.implementations = HashMultiset.create(); - ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, this.classEntry); - if (implementationsNode != null) { - @SuppressWarnings("unchecked") - Enumeration implementations = implementationsNode.children(); - while (implementations.hasMoreElements()) { - ClassImplementationsTreeNode node = implementations.nextElement(); - this.implementations.add(scrubClassName(node.getClassEntry().getName())); - } - } - - this.references = HashMultiset.create(); - if (useReferences) { - for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); - index.getFieldReferences(fieldEntry).forEach(this::addReference); - } - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - index.getBehaviorReferences(behaviorEntry).forEach(this::addReference); - } - } - - this.outer = null; - if (this.classEntry.isInnerClass()) { - this.outer = this.classEntry.getOuterClassName(); - } - } - - private void addReference(EntryReference reference) { - if (reference.context.getSignature() != null) { - this.references.add(String.format("%s_%s", - scrubClassName(reference.context.getClassName()), - scrubSignature(reference.context.getSignature()) - )); - } else { - this.references.add(String.format("%s_", - scrubClassName(reference.context.getClassName()) - )); - } - } - - public ClassEntry getClassEntry() { - return this.classEntry; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append("class: "); - buf.append(this.classEntry.getName()); - buf.append(" "); - buf.append(hashCode()); - buf.append("\n"); - for (String field : this.fields) { - buf.append("\tfield "); - buf.append(field); - buf.append("\n"); - } - for (String method : this.methods) { - buf.append("\tmethod "); - buf.append(method); - buf.append("\n"); - } - for (String constructor : this.constructors) { - buf.append("\tconstructor "); - buf.append(constructor); - buf.append("\n"); - } - if (!this.staticInitializer.isEmpty()) { - buf.append("\tinitializer "); - buf.append(this.staticInitializer); - buf.append("\n"); - } - if (!this.extendz.isEmpty()) { - buf.append("\textends "); - buf.append(this.extendz); - buf.append("\n"); - } - for (String interfaceName : this.implementz) { - buf.append("\timplements "); - buf.append(interfaceName); - buf.append("\n"); - } - for (String implementation : this.implementations) { - buf.append("\timplemented by "); - buf.append(implementation); - buf.append("\n"); - } - for (String reference : this.references) { - buf.append("\treference "); - buf.append(reference); - buf.append("\n"); - } - buf.append("\touter "); - buf.append(this.outer); - buf.append("\n"); - return buf.toString(); - } - - private String scrubClassName(String className) { - return classNameReplacer.replace(className); - } - - private String scrubType(String typeName) { - return scrubType(new Type(typeName)).toString(); - } - - private Type scrubType(Type type) { - if (type.hasClass()) { - return new Type(type, classNameReplacer); - } else { - return type; - } - } - - private String scrubSignature(String signature) { - return scrubSignature(new Signature(signature)).toString(); - } - - private Signature scrubSignature(Signature signature) { - return new Signature(signature, classNameReplacer); - } - - private boolean isClassMatchedUniquely(String className) { - return this.namer != null && this.namer.getName(Descriptor.toJvmName(className)) != null; - } - - private String getBehaviorSignature(CtBehavior behavior) { - try { - // does this method have an implementation? - if (behavior.getMethodInfo().getCodeAttribute() == null) { - return "(none)"; - } - - // compute the hash from the opcodes - ConstPool constants = behavior.getMethodInfo().getConstPool(); - final MessageDigest digest = MessageDigest.getInstance("MD5"); - CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); - while (iter.hasNext()) { - int pos = iter.next(); - - // update the hash with the opcode - int opcode = iter.byteAt(pos); - digest.update((byte) opcode); - int constIndex; - switch (opcode) { - case Opcode.LDC: - constIndex = iter.byteAt(pos + 1); - updateHashWithConstant(digest, constants, constIndex); - break; - - case Opcode.LDC_W: - case Opcode.LDC2_W: - constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); - updateHashWithConstant(digest, constants, constIndex); - break; - default: - break; - } - } - - // update hash with method and field accesses - behavior.instrument(new ExprEditor() { - @Override - public void edit(MethodCall call) { - updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); - updateHashWithString(digest, scrubSignature(call.getSignature())); - if (isClassMatchedUniquely(call.getClassName())) { - updateHashWithString(digest, call.getMethodName()); - } - } - - @Override - public void edit(FieldAccess access) { - updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName()))); - updateHashWithString(digest, scrubType(access.getSignature())); - if (isClassMatchedUniquely(access.getClassName())) { - updateHashWithString(digest, access.getFieldName()); - } - } - - @Override - public void edit(ConstructorCall call) { - updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); - updateHashWithString(digest, scrubSignature(call.getSignature())); - } - - @Override - public void edit(NewExpr expr) { - updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName()))); - } - }); - - // convert the hash to a hex string - return toHex(digest.digest()); - } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { - throw new Error(ex); - } - } - - private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { - ConstPoolEditor editor = new ConstPoolEditor(constants); - ConstInfoAccessor item = editor.getItem(index); - if (item.getType() == InfoType.StringInfo) { - updateHashWithString(digest, constants.getStringInfo(index)); - } - // TODO: other constants - } - - private void updateHashWithString(MessageDigest digest, String val) { - try { - digest.update(val.getBytes("UTF8")); - } catch (UnsupportedEncodingException ex) { - throw new Error(ex); - } - } - - private String toHex(byte[] bytes) { - // function taken from: - // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java - final char[] hexArray = "0123456789ABCDEF".toCharArray(); - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); - } - - @Override - public boolean equals(Object other) { - return other instanceof ClassIdentity && equals((ClassIdentity) other); - } - - public boolean equals(ClassIdentity other) { - return this.fields.equals(other.fields) - && this.methods.equals(other.methods) - && this.constructors.equals(other.constructors) - && this.staticInitializer.equals(other.staticInitializer) - && this.extendz.equals(other.extendz) - && this.implementz.equals(other.implementz) - && this.implementations.equals(other.implementations) - && this.references.equals(other.references); - } - - @Override - public int hashCode() { - List objs = Lists.newArrayList(); - objs.addAll(this.fields); - objs.addAll(this.methods); - objs.addAll(this.constructors); - objs.add(this.staticInitializer); - objs.add(this.extendz); - objs.addAll(this.implementz); - objs.addAll(this.implementations); - objs.addAll(this.references); - return Utils.combineHashesOrdered(objs); - } - - public int getMatchScore(ClassIdentity other) { - return 2 * getNumMatches(this.extendz, other.extendz) - + 2 * getNumMatches(this.outer, other.outer) - + 2 * getNumMatches(this.implementz, other.implementz) - + getNumMatches(this.stringLiterals, other.stringLiterals) - + getNumMatches(this.fields, other.fields) - + getNumMatches(this.methods, other.methods) - + getNumMatches(this.constructors, other.constructors); - } - - public int getMaxMatchScore() { - return 2 + 2 + 2 * this.implementz.size() + this.stringLiterals.size() + this.fields.size() + this.methods.size() + this.constructors.size(); - } - - public boolean matches(CtClass c) { - // just compare declaration counts - return this.fields.size() == c.getDeclaredFields().length - && this.methods.size() == c.getDeclaredMethods().length - && this.constructors.size() == c.getDeclaredConstructors().length; - } - - private int getNumMatches(Set a, Set b) { - int numMatches = 0; - for (String val : a) { - if (b.contains(val)) { - numMatches++; - } - } - return numMatches; - } - - private int getNumMatches(Multiset a, Multiset b) { - int numMatches = 0; - for (String val : a) { - if (b.contains(val)) { - numMatches++; - } - } - return numMatches; - } - - private int getNumMatches(String a, String b) { - if (a == null && b == null) { - return 1; - } else if (a != null && b != null && a.equals(b)) { - return 1; - } - return 0; - } -} diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatch.java b/src/main/java/cuchaz/enigma/convert/ClassMatch.java deleted file mode 100644 index bb3e4f4..0000000 --- a/src/main/java/cuchaz/enigma/convert/ClassMatch.java +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.Sets; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.utils.Utils; - -import java.util.Collection; -import java.util.Set; - -public class ClassMatch { - - public Set sourceClasses; - public Set destClasses; - - public ClassMatch(Collection sourceClasses, Collection destClasses) { - this.sourceClasses = Sets.newHashSet(sourceClasses); - this.destClasses = Sets.newHashSet(destClasses); - } - - public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) { - sourceClasses = Sets.newHashSet(); - if (sourceClass != null) { - sourceClasses.add(sourceClass); - } - destClasses = Sets.newHashSet(); - if (destClass != null) { - destClasses.add(destClass); - } - } - - public boolean isMatched() { - return !sourceClasses.isEmpty() && !destClasses.isEmpty(); - } - - public boolean isAmbiguous() { - return sourceClasses.size() > 1 || destClasses.size() > 1; - } - - public ClassEntry getUniqueSource() { - if (sourceClasses.size() != 1) { - throw new IllegalStateException("Match has ambiguous source!"); - } - return sourceClasses.iterator().next(); - } - - public ClassEntry getUniqueDest() { - if (destClasses.size() != 1) { - throw new IllegalStateException("Match has ambiguous source!"); - } - return destClasses.iterator().next(); - } - - public Set intersectSourceClasses(Set classes) { - Set intersection = Sets.newHashSet(sourceClasses); - intersection.retainAll(classes); - return intersection; - } - - @Override - public int hashCode() { - return Utils.combineHashesOrdered(sourceClasses, destClasses); - } - - @Override - public boolean equals(Object other) { - return other instanceof ClassMatch && equals((ClassMatch) other); - } - - public boolean equals(ClassMatch other) { - return this.sourceClasses.equals(other.sourceClasses) && this.destClasses.equals(other.destClasses); - } -} diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatches.java b/src/main/java/cuchaz/enigma/convert/ClassMatches.java deleted file mode 100644 index db2c550..0000000 --- a/src/main/java/cuchaz/enigma/convert/ClassMatches.java +++ /dev/null @@ -1,158 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import cuchaz.enigma.mapping.ClassEntry; - -import java.util.*; - -public class ClassMatches implements Iterable { - - private Collection matches; - private Map matchesBySource; - private Map matchesByDest; - private BiMap uniqueMatches; - private Map ambiguousMatchesBySource; - private Map ambiguousMatchesByDest; - private Set unmatchedSourceClasses; - private Set unmatchedDestClasses; - - public ClassMatches() { - this(new ArrayList<>()); - } - - public ClassMatches(Collection matches) { - this.matches = matches; - matchesBySource = Maps.newHashMap(); - matchesByDest = Maps.newHashMap(); - uniqueMatches = HashBiMap.create(); - ambiguousMatchesBySource = Maps.newHashMap(); - ambiguousMatchesByDest = Maps.newHashMap(); - unmatchedSourceClasses = Sets.newHashSet(); - unmatchedDestClasses = Sets.newHashSet(); - - for (ClassMatch match : matches) { - indexMatch(match); - } - } - - public void add(ClassMatch match) { - matches.add(match); - indexMatch(match); - } - - public void remove(ClassMatch match) { - for (ClassEntry sourceClass : match.sourceClasses) { - matchesBySource.remove(sourceClass); - uniqueMatches.remove(sourceClass); - ambiguousMatchesBySource.remove(sourceClass); - unmatchedSourceClasses.remove(sourceClass); - } - for (ClassEntry destClass : match.destClasses) { - matchesByDest.remove(destClass); - uniqueMatches.inverse().remove(destClass); - ambiguousMatchesByDest.remove(destClass); - unmatchedDestClasses.remove(destClass); - } - matches.remove(match); - } - - public int size() { - return matches.size(); - } - - @Override - public Iterator iterator() { - return matches.iterator(); - } - - private void indexMatch(ClassMatch match) { - if (!match.isMatched()) { - // unmatched - unmatchedSourceClasses.addAll(match.sourceClasses); - unmatchedDestClasses.addAll(match.destClasses); - } else { - if (match.isAmbiguous()) { - // ambiguously matched - for (ClassEntry entry : match.sourceClasses) { - ambiguousMatchesBySource.put(entry, match); - } - for (ClassEntry entry : match.destClasses) { - ambiguousMatchesByDest.put(entry, match); - } - } else { - // uniquely matched - uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); - } - } - for (ClassEntry entry : match.sourceClasses) { - matchesBySource.put(entry, match); - } - for (ClassEntry entry : match.destClasses) { - matchesByDest.put(entry, match); - } - } - - public BiMap getUniqueMatches() { - return uniqueMatches; - } - - public Set getUnmatchedSourceClasses() { - return unmatchedSourceClasses; - } - - public Set getUnmatchedDestClasses() { - return unmatchedDestClasses; - } - - public Set getAmbiguouslyMatchedSourceClasses() { - return ambiguousMatchesBySource.keySet(); - } - - public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { - return ambiguousMatchesBySource.get(sourceClass); - } - - public ClassMatch getMatchBySource(ClassEntry sourceClass) { - return matchesBySource.get(sourceClass); - } - - public ClassMatch getMatchByDest(ClassEntry destClass) { - return matchesByDest.get(destClass); - } - - public void removeSource(ClassEntry sourceClass) { - ClassMatch match = matchesBySource.get(sourceClass); - if (match != null) { - remove(match); - match.sourceClasses.remove(sourceClass); - if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { - add(match); - } - } - } - - public void removeDest(ClassEntry destClass) { - ClassMatch match = matchesByDest.get(destClass); - if (match != null) { - remove(match); - match.destClasses.remove(destClass); - if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { - add(match); - } - } - } -} diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatching.java b/src/main/java/cuchaz/enigma/convert/ClassMatching.java deleted file mode 100644 index f0f27cf..0000000 --- a/src/main/java/cuchaz/enigma/convert/ClassMatching.java +++ /dev/null @@ -1,153 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import cuchaz.enigma.mapping.ClassEntry; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -public class ClassMatching { - - private ClassForest sourceClasses; - private ClassForest destClasses; - private BiMap knownMatches; - - public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) { - sourceClasses = new ClassForest(sourceIdentifier); - destClasses = new ClassForest(destIdentifier); - knownMatches = HashBiMap.create(); - } - - public void addKnownMatches(BiMap knownMatches) { - this.knownMatches.putAll(knownMatches); - } - - public void match(Iterable sourceClasses, Iterable destClasses) { - for (ClassEntry sourceClass : sourceClasses) { - if (!knownMatches.containsKey(sourceClass)) { - this.sourceClasses.add(sourceClass); - } - } - for (ClassEntry destClass : destClasses) { - if (!knownMatches.containsValue(destClass)) { - this.destClasses.add(destClass); - } - } - } - - public Collection matches() { - List matches = Lists.newArrayList(); - for (Entry entry : knownMatches.entrySet()) { - matches.add(new ClassMatch( - entry.getKey(), - entry.getValue() - )); - } - for (ClassIdentity identity : sourceClasses.identities()) { - matches.add(new ClassMatch( - sourceClasses.getClasses(identity), - destClasses.getClasses(identity) - )); - } - for (ClassIdentity identity : destClasses.identities()) { - if (!sourceClasses.containsIdentity(identity)) { - matches.add(new ClassMatch( - new ArrayList<>(), - destClasses.getClasses(identity) - )); - } - } - return matches; - } - - public Collection sourceClasses() { - Set classes = Sets.newHashSet(); - for (ClassMatch match : matches()) { - classes.addAll(match.sourceClasses); - } - return classes; - } - - public Collection destClasses() { - Set classes = Sets.newHashSet(); - for (ClassMatch match : matches()) { - classes.addAll(match.destClasses); - } - return classes; - } - - public BiMap uniqueMatches() { - BiMap uniqueMatches = HashBiMap.create(); - for (ClassMatch match : matches()) { - if (match.isMatched() && !match.isAmbiguous()) { - uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); - } - } - return uniqueMatches; - } - - public Collection ambiguousMatches() { - List ambiguousMatches = Lists.newArrayList(); - for (ClassMatch match : matches()) { - if (match.isMatched() && match.isAmbiguous()) { - ambiguousMatches.add(match); - } - } - return ambiguousMatches; - } - - public Collection unmatchedSourceClasses() { - List classes = Lists.newArrayList(); - for (ClassMatch match : matches()) { - if (!match.isMatched() && !match.sourceClasses.isEmpty()) { - classes.addAll(match.sourceClasses); - } - } - return classes; - } - - public Collection unmatchedDestClasses() { - List classes = Lists.newArrayList(); - for (ClassMatch match : matches()) { - if (!match.isMatched() && !match.destClasses.isEmpty()) { - classes.addAll(match.destClasses); - } - } - return classes; - } - - @Override - public String toString() { - - // count the ambiguous classes - int numAmbiguousSource = 0; - int numAmbiguousDest = 0; - for (ClassMatch match : ambiguousMatches()) { - numAmbiguousSource += match.sourceClasses.size(); - numAmbiguousDest += match.destClasses.size(); - } - - return String.format("%20s%8s%8s\n", "", "Source", "Dest") + String - .format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size()) + String - .format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size()) + String - .format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest) + String - .format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size()); - } -} diff --git a/src/main/java/cuchaz/enigma/convert/ClassNamer.java b/src/main/java/cuchaz/enigma/convert/ClassNamer.java deleted file mode 100644 index e5902c4..0000000 --- a/src/main/java/cuchaz/enigma/convert/ClassNamer.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.BiMap; -import com.google.common.collect.Maps; -import cuchaz.enigma.mapping.ClassEntry; - -import java.util.Map; - -public class ClassNamer { - - private Map sourceNames; - private Map destNames; - - public ClassNamer(BiMap mappings) { - // convert the identity mappings to name maps - this.sourceNames = Maps.newHashMap(); - this.destNames = Maps.newHashMap(); - int i = 0; - for (Map.Entry entry : mappings.entrySet()) { - String name = String.format("M%04d", i++); - this.sourceNames.put(entry.getKey().getName(), name); - this.destNames.put(entry.getValue().getName(), name); - } - } - - public String getSourceName(String name) { - return this.sourceNames.get(name); - } - - public String getDestName(String name) { - return this.destNames.get(name); - } - - public SidedClassNamer getSourceNamer() { - return this::getSourceName; - } - - public SidedClassNamer getDestNamer() { - return this::getDestName; - } - - public interface SidedClassNamer { - String getName(String name); - } -} diff --git a/src/main/java/cuchaz/enigma/convert/FieldMatches.java b/src/main/java/cuchaz/enigma/convert/FieldMatches.java deleted file mode 100644 index a528b27..0000000 --- a/src/main/java/cuchaz/enigma/convert/FieldMatches.java +++ /dev/null @@ -1,150 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.*; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.FieldEntry; - -import java.util.Collection; -import java.util.Set; - -public class FieldMatches { - - private BiMap matches; - private Multimap matchedSourceFields; - private Multimap unmatchedSourceFields; - private Multimap unmatchedDestFields; - private Multimap unmatchableSourceFields; - - public FieldMatches() { - matches = HashBiMap.create(); - matchedSourceFields = HashMultimap.create(); - unmatchedSourceFields = HashMultimap.create(); - unmatchedDestFields = HashMultimap.create(); - unmatchableSourceFields = HashMultimap.create(); - } - - public void addMatch(FieldEntry srcField, FieldEntry destField) { - boolean wasAdded = matches.put(srcField, destField) == null; - assert (wasAdded); - wasAdded = matchedSourceFields.put(srcField.getClassEntry(), srcField); - assert (wasAdded); - } - - public void addUnmatchedSourceField(FieldEntry fieldEntry) { - boolean wasAdded = unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); - assert (wasAdded); - } - - public void addUnmatchedSourceFields(Iterable fieldEntries) { - for (FieldEntry fieldEntry : fieldEntries) { - addUnmatchedSourceField(fieldEntry); - } - } - - public void addUnmatchedDestField(FieldEntry fieldEntry) { - boolean wasAdded = unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); - assert (wasAdded); - } - - public void addUnmatchedDestFields(Iterable fieldEntries) { - for (FieldEntry fieldEntry : fieldEntries) { - addUnmatchedDestField(fieldEntry); - } - } - - public void addUnmatchableSourceField(FieldEntry sourceField) { - boolean wasAdded = unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField); - assert (wasAdded); - } - - public Set getSourceClassesWithUnmatchedFields() { - return unmatchedSourceFields.keySet(); - } - - public Collection getSourceClassesWithoutUnmatchedFields() { - Set out = Sets.newHashSet(); - out.addAll(matchedSourceFields.keySet()); - out.removeAll(unmatchedSourceFields.keySet()); - return out; - } - - public Collection getUnmatchedSourceFields() { - return unmatchedSourceFields.values(); - } - - public Collection getUnmatchedSourceFields(ClassEntry sourceClass) { - return unmatchedSourceFields.get(sourceClass); - } - - public Collection getUnmatchedDestFields() { - return unmatchedDestFields.values(); - } - - public Collection getUnmatchedDestFields(ClassEntry destClass) { - return unmatchedDestFields.get(destClass); - } - - public Collection getUnmatchableSourceFields() { - return unmatchableSourceFields.values(); - } - - public boolean hasSource(FieldEntry fieldEntry) { - return matches.containsKey(fieldEntry) || unmatchedSourceFields.containsValue(fieldEntry); - } - - public boolean hasDest(FieldEntry fieldEntry) { - return matches.containsValue(fieldEntry) || unmatchedDestFields.containsValue(fieldEntry); - } - - public BiMap matches() { - return matches; - } - - public boolean isMatchedSourceField(FieldEntry sourceField) { - return matches.containsKey(sourceField); - } - - public boolean isMatchedDestField(FieldEntry destField) { - return matches.containsValue(destField); - } - - public void makeMatch(FieldEntry sourceField, FieldEntry destField) { - boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); - assert (wasRemoved); - wasRemoved = unmatchedDestFields.remove(destField.getClassEntry(), destField); - assert (wasRemoved); - addMatch(sourceField, destField); - } - - public boolean isMatched(FieldEntry sourceField, FieldEntry destField) { - FieldEntry match = matches.get(sourceField); - return match != null && match.equals(destField); - } - - public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) { - boolean wasRemoved = matches.remove(sourceField) != null; - assert (wasRemoved); - wasRemoved = matchedSourceFields.remove(sourceField.getClassEntry(), sourceField); - assert (wasRemoved); - addUnmatchedSourceField(sourceField); - addUnmatchedDestField(destField); - } - - public void makeSourceUnmatchable(FieldEntry sourceField) { - assert (!isMatchedSourceField(sourceField)); - boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); - assert (wasRemoved); - addUnmatchableSourceField(sourceField); - } -} diff --git a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java deleted file mode 100644 index fa3e936..0000000 --- a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java +++ /dev/null @@ -1,711 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.*; -import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.TranslatingTypeLoader; -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; -import cuchaz.enigma.mapping.*; -import cuchaz.enigma.throwables.MappingConflict; -import javassist.CtClass; -import javassist.CtMethod; -import javassist.NotFoundException; -import javassist.bytecode.BadBytecode; -import javassist.bytecode.CodeAttribute; -import javassist.bytecode.CodeIterator; - -import java.util.*; -import java.util.jar.JarFile; - -public class MappingsConverter { - - public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { - - // index jars - System.out.println("Indexing source jar..."); - JarIndex sourceIndex = new JarIndex(); - sourceIndex.indexJar(sourceJar, false); - System.out.println("Indexing dest jar..."); - JarIndex destIndex = new JarIndex(); - destIndex.indexJar(destJar, false); - - // compute the matching - ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); - return new ClassMatches(matching.matches()); - } - - public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap knownMatches) { - - System.out.println("Iteratively matching classes"); - - ClassMatching lastMatching = null; - int round = 0; - SidedClassNamer sourceNamer = null; - SidedClassNamer destNamer = null; - for (boolean useReferences : Arrays.asList(false, true)) { - - int numUniqueMatchesLastTime = 0; - if (lastMatching != null) { - numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); - } - - while (true) { - - System.out.println("Round " + (++round) + "..."); - - // init the matching with identity settings - ClassMatching matching = new ClassMatching( - new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), - new ClassIdentifier(destJar, destIndex, destNamer, useReferences) - ); - - if (knownMatches != null) { - matching.addKnownMatches(knownMatches); - } - - if (lastMatching == null) { - // search all classes - matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); - } else { - // we already know about these matches from last time - matching.addKnownMatches(lastMatching.uniqueMatches()); - - // search unmatched and ambiguously-matched classes - matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); - for (ClassMatch match : lastMatching.ambiguousMatches()) { - matching.match(match.sourceClasses, match.destClasses); - } - } - System.out.println(matching); - BiMap uniqueMatches = matching.uniqueMatches(); - - // did we match anything new this time? - if (uniqueMatches.size() > numUniqueMatchesLastTime) { - numUniqueMatchesLastTime = uniqueMatches.size(); - lastMatching = matching; - } else { - break; - } - - // update the namers - ClassNamer namer = new ClassNamer(uniqueMatches); - sourceNamer = namer.getSourceNamer(); - destNamer = namer.getDestNamer(); - } - } - - return lastMatching; - } - - public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) - throws MappingConflict { - // sort the unique matches by size of inner class chain - Multimap> matchesByDestChainSize = HashMultimap.create(); - for (java.util.Map.Entry match : matches.getUniqueMatches().entrySet()) { - int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); - matchesByDestChainSize.put(chainSize, match); - } - - // build the mappings (in order of small-to-large inner chains) - Mappings newMappings = new Mappings(); - List chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); - Collections.sort(chainSizes); - for (int chainSize : chainSizes) { - for (java.util.Map.Entry match : matchesByDestChainSize.get(chainSize)) { - // get class info - ClassEntry obfSourceClassEntry = match.getKey(); - ClassEntry obfDestClassEntry = match.getValue(); - List destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); - - ClassMapping sourceMapping; - if (obfSourceClassEntry.isInnerClass()) { - List srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry); - sourceMapping = srcClassChain.get(srcClassChain.size() - 1); - } else { - sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); - } - - if (sourceMapping == null) { - // if this class was never deobfuscated, don't try to match it - continue; - } - - // find out where to make the dest class mapping - if (destClassChain.size() == 1) { - // not an inner class, add directly to mappings - newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); - } else { - // inner class, find the outer class mapping - ClassMapping destMapping = null; - for (int i = 0; i < destClassChain.size() - 1; i++) { - ClassEntry destChainClassEntry = destClassChain.get(i); - if (destMapping == null) { - destMapping = newMappings.getClassByObf(destChainClassEntry); - if (destMapping == null) { - destMapping = new ClassMapping(destChainClassEntry.getName()); - newMappings.addClassMapping(destMapping); - } - } else { - destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName()); - if (destMapping == null) { - destMapping = new ClassMapping(destChainClassEntry.getName()); - destMapping.addInnerClassMapping(destMapping); - } - } - } - destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); - } - } - } - return newMappings; - } - - private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) { - - ClassNameReplacer replacer = className -> - { - ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); - if (newClassEntry != null) { - return newClassEntry.getName(); - } - return null; - }; - - ClassMapping newClassMapping; - String deobfName = oldClassMapping.getDeobfName(); - if (deobfName != null) { - if (useSimpleName) { - deobfName = new ClassEntry(deobfName).getSimpleName(); - } - newClassMapping = new ClassMapping(newObfClass.getName(), deobfName); - } else { - newClassMapping = new ClassMapping(newObfClass.getName()); - } - - // migrate fields - for (FieldMapping oldFieldMapping : oldClassMapping.fields()) { - if (canMigrate(oldFieldMapping.getObfType(), matches)) { - newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer)); - } else { - System.out.println(String.format("Can't map field, dropping: %s.%s %s", - oldClassMapping.getDeobfName(), - oldFieldMapping.getDeobfName(), - oldFieldMapping.getObfType() - )); - } - } - - // migrate methods - for (MethodMapping oldMethodMapping : oldClassMapping.methods()) { - if (canMigrate(oldMethodMapping.getObfSignature(), matches)) { - newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer)); - } else { - System.out.println(String.format("Can't map method, dropping: %s.%s %s", - oldClassMapping.getDeobfName(), - oldMethodMapping.getDeobfName(), - oldMethodMapping.getObfSignature() - )); - } - } - - return newClassMapping; - } - - private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) { - for (Type oldObfType : oldObfSignature.types()) { - if (!canMigrate(oldObfType, classMatches)) { - return false; - } - } - return true; - } - - private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) { - - // non classes can be migrated - if (!oldObfType.hasClass()) { - return true; - } - - // non obfuscated classes can be migrated - ClassEntry classEntry = oldObfType.getClassEntry(); - if (classEntry.getPackageName() != null) { - return true; - } - - // obfuscated classes with mappings can be migrated - return classMatches.getUniqueMatches().containsKey(classEntry); - } - - public static void convertMappings(Mappings mappings, BiMap changes) { - - // sort the changes so classes are renamed in the correct order - // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b - LinkedHashMap sortedChanges = Maps.newLinkedHashMap(); - int numChangesLeft = changes.size(); - while (!changes.isEmpty()) { - Iterator> iter = changes.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry change = iter.next(); - if (changes.containsKey(change.getValue())) { - sortedChanges.put(change.getKey(), change.getValue()); - iter.remove(); - } - } - - // did we remove any changes? - if (numChangesLeft - changes.size() > 0) { - // keep going - numChangesLeft = changes.size(); - } else { - // can't sort anymore. There must be a loop - break; - } - } - if (!changes.isEmpty()) { - throw new Error("Unable to sort class changes! There must be a cycle."); - } - - // convert the mappings in the correct class order - for (Map.Entry entry : sortedChanges.entrySet()) { - mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); - } - } - - public static Doer getFieldDoer() { - return new Doer() { - - @Override - public Collection getDroppedEntries(MappingsChecker checker) { - return checker.getDroppedFieldMappings().keySet(); - } - - @Override - public Collection getObfEntries(JarIndex jarIndex) { - return jarIndex.getObfFieldEntries(); - } - - @Override - public Collection> getMappings(ClassMapping destClassMapping) { - return (Collection>) destClassMapping.fields(); - } - - @Override - public Set filterEntries(Collection obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) { - Set out = Sets.newHashSet(); - for (FieldEntry obfDestField : obfDestFields) { - Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); - if (translatedDestType.equals(obfSourceField.getType())) { - out.add(obfDestField); - } - } - return out; - } - - @Override - public void setUpdateObfMember(ClassMapping classMapping, MemberMapping memberMapping, FieldEntry newField) { - FieldMapping fieldMapping = (FieldMapping) memberMapping; - classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType()); - } - - @Override - public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { - return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; - } - - @Override - public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) { - classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType())); - } - }; - } - - public static Doer getMethodDoer() { - return new Doer() { - - @Override - public Collection getDroppedEntries(MappingsChecker checker) { - return checker.getDroppedMethodMappings().keySet(); - } - - @Override - public Collection getObfEntries(JarIndex jarIndex) { - return jarIndex.getObfBehaviorEntries(); - } - - @Override - public Collection> getMappings(ClassMapping destClassMapping) { - return (Collection>) destClassMapping.methods(); - } - - @Override - public Set filterEntries(Collection obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) { - Set out = Sets.newHashSet(); - for (BehaviorEntry obfDestField : obfDestFields) { - // Try to translate the signature - Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse()); - if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature())) - out.add(obfDestField); - } - return out; - } - - @Override - public void setUpdateObfMember(ClassMapping classMapping, MemberMapping memberMapping, BehaviorEntry newBehavior) { - MethodMapping methodMapping = (MethodMapping) memberMapping; - classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature()); - } - - @Override - public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { - return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; - } - - @Override - public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) { - classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature())); - } - }; - } - - public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) { - int sourcePos = 0; - int destPos = 0; - while (sourceIt.hasNext() && destIt.hasNext()) { - try { - sourcePos = sourceIt.next(); - destPos = destIt.next(); - if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos)) - return sourcePos; - } catch (BadBytecode badBytecode) { - // Ignore bad bytecode (it might be a little bit dangerous...) - } - } - if (sourcePos < destPos) - return sourcePos; - else if (destPos < sourcePos) - return destPos; - return sourcePos; - } - - public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry, - Set obfDestEntries) { - try { - // Get the source method with Javassist - CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString()); - CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute(); - - // Empty method body, ignore! - if (sourceAttribute == null) - return null; - for (BehaviorEntry desEntry : obfDestEntries) { - try { - CtMethod destCtClassMethod = destCtClass - .getMethod(desEntry.getName(), desEntry.getSignature().toString()); - CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute(); - - // Ignore empty body methods - if (destAttribute == null) - continue; - CodeIterator destIterator = destAttribute.iterator(); - int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator); - - // The bytecode is identical to the original method, assuming that the method is correct! - if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1) - return desEntry; - } catch (NotFoundException e) { - e.printStackTrace(); - } - } - } catch (NotFoundException e) { - e.printStackTrace(); - return null; - } - return null; - } - - public static MemberMatches computeMethodsMatches(Deobfuscator destDeobfuscator, - Mappings destMappings, - Deobfuscator sourceDeobfuscator, - Mappings sourceMappings, - ClassMatches classMatches, - Doer doer) { - - MemberMatches memberMatches = new MemberMatches<>(); - - // unmatched source fields are easy - MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); - checker.dropBrokenMappings(destMappings); - for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) { - BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); - memberMatches.addUnmatchedSourceEntry(srcObfEntry); - } - - // get matched fields (anything that's left after the checks/drops is matched( - for (ClassMapping classMapping : destMappings.classes()) - collectMatchedFields(memberMatches, classMapping, classMatches, doer); - - // get unmatched dest fields - doer.getObfEntries(destDeobfuscator.getJarIndex()).stream() - .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry)) - .forEach(memberMatches::addUnmatchedDestEntry); - - // Apply mappings to deobfuscator - - // Create type loader - TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader(); - TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader(); - - System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); - - // go through the unmatched source fields and try to pick out the easy matches - for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { - for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { - - // get the possible dest matches - ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); - - // filter by type/signature - Set obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); - - if (obfDestEntries.size() == 1) { - // make the easy match - memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); - } else if (obfDestEntries.isEmpty()) { - // no match is possible =( - memberMatches.makeSourceUnmatchable(obfSourceEntry, null); - } else { - // Multiple matches! Scan methods instructions - CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName()); - CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName()); - BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries); - // the method match correctly, match it on the member mapping! - if (match != null) - memberMatches.makeMatch(obfSourceEntry, match); - } - } - } - - System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", - memberMatches.getUnmatchedSourceEntries().size(), - memberMatches.getUnmatchableSourceEntries().size() - )); - - return memberMatches; - } - - public static MemberMatches computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer doer) { - - MemberMatches memberMatches = new MemberMatches<>(); - - // unmatched source fields are easy - MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); - checker.dropBrokenMappings(destMappings); - for (T destObfEntry : doer.getDroppedEntries(checker)) { - T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); - memberMatches.addUnmatchedSourceEntry(srcObfEntry); - } - - // get matched fields (anything that's left after the checks/drops is matched( - for (ClassMapping classMapping : destMappings.classes()) { - collectMatchedFields(memberMatches, classMapping, classMatches, doer); - } - - // get unmatched dest fields - for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) { - if (!memberMatches.isMatchedDestEntry(destEntry)) { - memberMatches.addUnmatchedDestEntry(destEntry); - } - } - - System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); - - // go through the unmatched source fields and try to pick out the easy matches - for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { - for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { - - // get the possible dest matches - ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); - - // filter by type/signature - Set obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); - - if (obfDestEntries.size() == 1) { - // make the easy match - memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); - } else if (obfDestEntries.isEmpty()) { - // no match is possible =( - memberMatches.makeSourceUnmatchable(obfSourceEntry, null); - } - } - } - - System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", - memberMatches.getUnmatchedSourceEntries().size(), - memberMatches.getUnmatchableSourceEntries().size() - )); - - return memberMatches; - } - - private static void collectMatchedFields(MemberMatches memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer doer) { - - // get the fields for this class - for (MemberMapping destEntryMapping : doer.getMappings(destClassMapping)) { - T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry()); - T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); - memberMatches.addMatch(srcObfField, destObfField); - } - - // recurse - for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { - collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer); - } - } - - @SuppressWarnings("unchecked") - private static T translate(T in, BiMap map) { - if (in instanceof FieldEntry) { - return (T) new FieldEntry( - map.get(in.getClassEntry()), - in.getName(), - translate(((FieldEntry) in).getType(), map) - ); - } else if (in instanceof MethodEntry) { - return (T) new MethodEntry( - map.get(in.getClassEntry()), - in.getName(), - translate(((MethodEntry) in).getSignature(), map) - ); - } else if (in instanceof ConstructorEntry) { - return (T) new ConstructorEntry( - map.get(in.getClassEntry()), - translate(((ConstructorEntry) in).getSignature(), map) - ); - } - throw new Error("Unhandled entry type: " + in.getClass()); - } - - private static Type translate(Type type, final BiMap map) { - return new Type(type, inClassName -> - { - ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); - if (outClassEntry == null) { - return null; - } - return outClassEntry.getName(); - }); - } - - private static Signature translate(Signature signature, final BiMap map) { - if (signature == null) { - return null; - } - return new Signature(signature, inClassName -> - { - ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); - if (outClassEntry == null) { - return null; - } - return outClassEntry.getName(); - }); - } - - public static void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches memberMatches, Doer doer) { - for (ClassMapping classMapping : mappings.classes()) { - applyMemberMatches(classMapping, classMatches, memberMatches, doer); - } - } - - private static void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches memberMatches, Doer doer) { - - // get the classes - ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName()); - - // make a map of all the renames we need to make - Map renames = Maps.newHashMap(); - for (MemberMapping memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { - T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); - T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches); - - // but drop the unmatchable things - if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) { - doer.removeMemberByObf(classMapping, obfOldDestEntry); - continue; - } - - T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry); - if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) { - renames.put(obfOldDestEntry, obfNewDestEntry); - } - } - - if (!renames.isEmpty()) { - - // apply to this class (should never need more than n passes) - int numRenamesAppliedThisRound; - do { - numRenamesAppliedThisRound = 0; - - for (MemberMapping memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { - T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); - T obfNewDestEntry = renames.get(obfOldDestEntry); - if (obfNewDestEntry != null) { - // make sure this rename won't cause a collision - // otherwise, save it for the next round and try again next time - if (!doer.hasObfMember(classMapping, obfNewDestEntry)) { - doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry); - renames.remove(obfOldDestEntry); - numRenamesAppliedThisRound++; - } - } - } - } while (numRenamesAppliedThisRound > 0); - - if (!renames.isEmpty()) { - System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.", - classMapping.getObfFullName(), renames.size() - )); - for (Map.Entry entry : renames.entrySet()) { - System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName())); - } - } - } - - // recurse - for (ClassMapping innerClassMapping : classMapping.innerClasses()) { - applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer); - } - } - - private static T getSourceEntryFromDestMapping(MemberMapping destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) { - return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse()); - } - - public interface Doer { - Collection getDroppedEntries(MappingsChecker checker); - - Collection getObfEntries(JarIndex jarIndex); - - Collection> getMappings(ClassMapping destClassMapping); - - Set filterEntries(Collection obfEntries, T obfSourceEntry, ClassMatches classMatches); - - void setUpdateObfMember(ClassMapping classMapping, MemberMapping memberMapping, T newEntry); - - boolean hasObfMember(ClassMapping classMapping, T obfEntry); - - void removeMemberByObf(ClassMapping classMapping, T obfEntry); - } -} diff --git a/src/main/java/cuchaz/enigma/convert/MatchesReader.java b/src/main/java/cuchaz/enigma/convert/MatchesReader.java deleted file mode 100644 index 1cf50fa..0000000 --- a/src/main/java/cuchaz/enigma/convert/MatchesReader.java +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.Lists; -import cuchaz.enigma.mapping.*; - -import java.io.*; -import java.nio.charset.Charset; -import java.util.Collection; -import java.util.List; - -public class MatchesReader { - - public static ClassMatches readClasses(File file) - throws IOException { - try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { - ClassMatches matches = new ClassMatches(); - String line; - while ((line = in.readLine()) != null) { - matches.add(readClassMatch(line)); - } - return matches; - } - } - - private static ClassMatch readClassMatch(String line) { - String[] sides = line.split(":", 2); - return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); - } - - private static Collection readClasses(String in) { - List entries = Lists.newArrayList(); - for (String className : in.split(",")) { - className = className.trim(); - if (!className.isEmpty()) { - entries.add(new ClassEntry(className)); - } - } - return entries; - } - - public static MemberMatches readMembers(File file) - throws IOException { - try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { - MemberMatches matches = new MemberMatches<>(); - String line; - while ((line = in.readLine()) != null) { - readMemberMatch(matches, line); - } - return matches; - } - } - - private static void readMemberMatch(MemberMatches matches, String line) { - if (line.startsWith("!")) { - T source = readEntry(line.substring(1)); - matches.addUnmatchableSourceEntry(source); - } else { - String[] parts = line.split(":", 2); - T source = readEntry(parts[0]); - T dest = readEntry(parts[1]); - if (source != null && dest != null) { - matches.addMatch(source, dest); - } else if (source != null) { - matches.addUnmatchedSourceEntry(source); - } else if (dest != null) { - matches.addUnmatchedDestEntry(dest); - } - } - } - - @SuppressWarnings("unchecked") - private static T readEntry(String in) { - if (in.length() <= 0) { - return null; - } - String[] parts = in.split(" "); - if (parts.length == 3 && parts[2].indexOf('(') < 0) { - return (T) new FieldEntry( - new ClassEntry(parts[0]), - parts[1], - new Type(parts[2]) - ); - } else { - assert (parts.length == 2 || parts.length == 3); - if (parts.length == 2) { - return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1]); - } else if (parts.length == 3) { - return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]); - } else { - throw new Error("Malformed behavior entry: " + in); - } - } - } -} diff --git a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java b/src/main/java/cuchaz/enigma/convert/MatchesWriter.java deleted file mode 100644 index 8fe7326..0000000 --- a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; -import cuchaz.enigma.mapping.FieldEntry; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.nio.charset.Charset; -import java.util.Map; - -public class MatchesWriter { - - public static void writeClasses(ClassMatches matches, File file) - throws IOException { - try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { - for (ClassMatch match : matches) { - writeClassMatch(out, match); - } - } - } - - private static void writeClassMatch(OutputStreamWriter out, ClassMatch match) - throws IOException { - writeClasses(out, match.sourceClasses); - out.write(":"); - writeClasses(out, match.destClasses); - out.write("\n"); - } - - private static void writeClasses(OutputStreamWriter out, Iterable classes) - throws IOException { - boolean isFirst = true; - for (ClassEntry entry : classes) { - if (isFirst) { - isFirst = false; - } else { - out.write(","); - } - out.write(entry.toString()); - } - } - - public static void writeMembers(MemberMatches matches, File file) - throws IOException { - try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { - for (Map.Entry match : matches.matches().entrySet()) { - writeMemberMatch(out, match.getKey(), match.getValue()); - } - for (T entry : matches.getUnmatchedSourceEntries()) { - writeMemberMatch(out, entry, null); - } - for (T entry : matches.getUnmatchedDestEntries()) { - writeMemberMatch(out, null, entry); - } - for (T entry : matches.getUnmatchableSourceEntries()) { - writeUnmatchableEntry(out, entry); - } - } - } - - private static void writeMemberMatch(OutputStreamWriter out, T source, T dest) - throws IOException { - if (source != null) { - writeEntry(out, source); - } - out.write(":"); - if (dest != null) { - writeEntry(out, dest); - } - out.write("\n"); - } - - private static void writeUnmatchableEntry(OutputStreamWriter out, T entry) - throws IOException { - out.write("!"); - writeEntry(out, entry); - out.write("\n"); - } - - private static void writeEntry(OutputStreamWriter out, T entry) - throws IOException { - if (entry instanceof FieldEntry) { - writeField(out, (FieldEntry) entry); - } else if (entry instanceof BehaviorEntry) { - writeBehavior(out, (BehaviorEntry) entry); - } - } - - private static void writeField(OutputStreamWriter out, FieldEntry fieldEntry) - throws IOException { - out.write(fieldEntry.getClassName()); - out.write(" "); - out.write(fieldEntry.getName()); - out.write(" "); - out.write(fieldEntry.getType().toString()); - } - - private static void writeBehavior(OutputStreamWriter out, BehaviorEntry behaviorEntry) - throws IOException { - out.write(behaviorEntry.getClassName()); - out.write(" "); - out.write(behaviorEntry.getName()); - out.write(" "); - if (behaviorEntry.getSignature() != null) { - out.write(behaviorEntry.getSignature().toString()); - } - } -} diff --git a/src/main/java/cuchaz/enigma/convert/MemberMatches.java b/src/main/java/cuchaz/enigma/convert/MemberMatches.java deleted file mode 100644 index bd74311..0000000 --- a/src/main/java/cuchaz/enigma/convert/MemberMatches.java +++ /dev/null @@ -1,179 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.convert; - -import com.google.common.collect.*; -import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; - -import java.util.Collection; -import java.util.Set; - -public class MemberMatches { - - private BiMap matches; - private Multimap matchedSourceEntries; - private Multimap unmatchedSourceEntries; - private Multimap unmatchedDestEntries; - private Multimap unmatchableSourceEntries; - - public MemberMatches() { - matches = HashBiMap.create(); - matchedSourceEntries = HashMultimap.create(); - unmatchedSourceEntries = HashMultimap.create(); - unmatchedDestEntries = HashMultimap.create(); - unmatchableSourceEntries = HashMultimap.create(); - } - - public void addMatch(T srcEntry, T destEntry) { - boolean wasAdded = matches.put(srcEntry, destEntry) == null; - assert (wasAdded); - wasAdded = matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry); - assert (wasAdded); - } - - public void addUnmatchedSourceEntry(T sourceEntry) { - boolean wasAdded = unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); - assert (wasAdded); - } - - public void addUnmatchedSourceEntries(Iterable sourceEntries) { - for (T sourceEntry : sourceEntries) { - addUnmatchedSourceEntry(sourceEntry); - } - } - - public void addUnmatchedDestEntry(T destEntry) { - if (destEntry.getName().equals("") || destEntry.getName().equals("")) - return; - boolean wasAdded = unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry); - assert (wasAdded); - } - - public void addUnmatchedDestEntries(Iterable destEntriesntries) { - for (T entry : destEntriesntries) { - addUnmatchedDestEntry(entry); - } - } - - public void addUnmatchableSourceEntry(T sourceEntry) { - boolean wasAdded = unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); - assert (wasAdded); - } - - public Set getSourceClassesWithUnmatchedEntries() { - return unmatchedSourceEntries.keySet(); - } - - public Collection getSourceClassesWithoutUnmatchedEntries() { - Set out = Sets.newHashSet(); - out.addAll(matchedSourceEntries.keySet()); - out.removeAll(unmatchedSourceEntries.keySet()); - return out; - } - - public Collection getUnmatchedSourceEntries() { - return unmatchedSourceEntries.values(); - } - - public Collection getUnmatchedSourceEntries(ClassEntry sourceClass) { - return unmatchedSourceEntries.get(sourceClass); - } - - public Collection getUnmatchedDestEntries() { - return unmatchedDestEntries.values(); - } - - public Collection getUnmatchedDestEntries(ClassEntry destClass) { - return unmatchedDestEntries.get(destClass); - } - - public Collection getUnmatchableSourceEntries() { - return unmatchableSourceEntries.values(); - } - - public boolean hasSource(T sourceEntry) { - return matches.containsKey(sourceEntry) || unmatchedSourceEntries.containsValue(sourceEntry); - } - - public boolean hasDest(T destEntry) { - return matches.containsValue(destEntry) || unmatchedDestEntries.containsValue(destEntry); - } - - public BiMap matches() { - return matches; - } - - public boolean isMatchedSourceEntry(T sourceEntry) { - return matches.containsKey(sourceEntry); - } - - public boolean isMatchedDestEntry(T destEntry) { - return matches.containsValue(destEntry); - } - - public boolean isUnmatchableSourceEntry(T sourceEntry) { - return unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry); - } - - public void makeMatch(T sourceEntry, T destEntry) { - makeMatch(sourceEntry, destEntry, null, null); - } - - public void makeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - if (sourceDeobfuscator != null && destDeobfuscator != null) { - makeMatch(sourceEntry, destEntry); - sourceEntry = (T) sourceEntry.cloneToNewClass(sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); - destEntry = (T) destEntry.cloneToNewClass(destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); - } - boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); - assert (wasRemoved); - wasRemoved = unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry); - assert (wasRemoved); - addMatch(sourceEntry, destEntry); - } - - public boolean isMatched(T sourceEntry, T destEntry) { - T match = matches.get(sourceEntry); - return match != null && match.equals(destEntry); - } - - public void unmakeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - if (sourceDeobfuscator != null && destDeobfuscator != null) { - unmakeMatch(sourceEntry, destEntry, null, null); - sourceEntry = (T) sourceEntry.cloneToNewClass( - sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); - destEntry = (T) destEntry.cloneToNewClass( - destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); - } - - boolean wasRemoved = matches.remove(sourceEntry) != null; - assert (wasRemoved); - wasRemoved = matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); - assert (wasRemoved); - addUnmatchedSourceEntry(sourceEntry); - addUnmatchedDestEntry(destEntry); - } - - public void makeSourceUnmatchable(T sourceEntry, Deobfuscator sourceDeobfuscator) { - if (sourceDeobfuscator != null) { - makeSourceUnmatchable(sourceEntry, null); - sourceEntry = (T) sourceEntry.cloneToNewClass( - sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); - } - assert (!isMatchedSourceEntry(sourceEntry)); - boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); - assert (wasRemoved); - addUnmatchableSourceEntry(sourceEntry); - } -} diff --git a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java deleted file mode 100644 index 833a534..0000000 --- a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java +++ /dev/null @@ -1,536 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.gui; - -import com.google.common.collect.BiMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import cuchaz.enigma.Constants; -import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.convert.*; -import cuchaz.enigma.gui.node.ClassSelectorClassNode; -import cuchaz.enigma.gui.node.ClassSelectorPackageNode; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Mappings; -import cuchaz.enigma.mapping.MappingsChecker; -import cuchaz.enigma.throwables.MappingConflict; -import de.sciss.syntaxpane.DefaultSyntaxKit; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionListener; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -public class ClassMatchingGui { - - // controls - private JFrame frame; - private ClassSelector sourceClasses; - private ClassSelector destClasses; - private CodeReader sourceReader; - private CodeReader destReader; - private JLabel sourceClassLabel; - private JLabel destClassLabel; - private JButton matchButton; - private Map sourceTypeButtons; - private JCheckBox advanceCheck; - private JCheckBox top10Matches; - private ClassMatches classMatches; - private Deobfuscator sourceDeobfuscator; - private Deobfuscator destDeobfuscator; - private ClassEntry sourceClass; - private ClassEntry destClass; - private SourceType sourceType; - private SaveListener saveListener; - - public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - - classMatches = matches; - this.sourceDeobfuscator = sourceDeobfuscator; - this.destDeobfuscator = destDeobfuscator; - - // init frame - frame = new JFrame(Constants.NAME + " - Class Matcher"); - final Container pane = frame.getContentPane(); - pane.setLayout(new BorderLayout()); - - // init source side - JPanel sourcePanel = new JPanel(); - sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); - sourcePanel.setPreferredSize(new Dimension(200, 0)); - pane.add(sourcePanel, BorderLayout.WEST); - sourcePanel.add(new JLabel("Source Classes")); - - // init source type radios - JPanel sourceTypePanel = new JPanel(); - sourcePanel.add(sourceTypePanel); - sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); - ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); - ButtonGroup sourceTypeButtons = new ButtonGroup(); - this.sourceTypeButtons = Maps.newHashMap(); - for (SourceType sourceType : SourceType.values()) { - JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); - this.sourceTypeButtons.put(sourceType, button); - sourceTypePanel.add(button); - } - - sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); - sourceClasses.setSelectionListener(this::setSourceClass); - JScrollPane sourceScroller = new JScrollPane(sourceClasses); - sourcePanel.add(sourceScroller); - - // init dest side - JPanel destPanel = new JPanel(); - destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); - destPanel.setPreferredSize(new Dimension(200, 0)); - pane.add(destPanel, BorderLayout.WEST); - destPanel.add(new JLabel("Destination Classes")); - - top10Matches = new JCheckBox("Show only top 10 matches"); - destPanel.add(top10Matches); - top10Matches.addActionListener(event -> toggleTop10Matches()); - - destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); - destClasses.setSelectionListener(this::setDestClass); - JScrollPane destScroller = new JScrollPane(destClasses); - destPanel.add(destScroller); - - JButton autoMatchButton = new JButton("AutoMatch"); - autoMatchButton.addActionListener(event -> autoMatch()); - destPanel.add(autoMatchButton); - - // init source panels - DefaultSyntaxKit.initKit(); - sourceReader = new CodeReader(); - destReader = new CodeReader(); - - // init all the splits - JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane( - sourceReader)); - splitLeft.setResizeWeight(0); // let the right side take all the slack - JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(destReader), destPanel); - splitRight.setResizeWeight(1); // let the left side take all the slack - JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); - splitCenter.setResizeWeight(0.5); // resize 50:50 - pane.add(splitCenter, BorderLayout.CENTER); - splitCenter.resetToPreferredSizes(); - - // init bottom panel - JPanel bottomPanel = new JPanel(); - bottomPanel.setLayout(new FlowLayout()); - - sourceClassLabel = new JLabel(); - sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); - destClassLabel = new JLabel(); - destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); - - matchButton = new JButton(); - - advanceCheck = new JCheckBox("Advance to next likely match"); - advanceCheck.addActionListener(event -> { - if (advanceCheck.isSelected()) { - advance(); - } - }); - - bottomPanel.add(sourceClassLabel); - bottomPanel.add(matchButton); - bottomPanel.add(destClassLabel); - bottomPanel.add(advanceCheck); - pane.add(bottomPanel, BorderLayout.SOUTH); - - // show the frame - pane.doLayout(); - frame.setSize(1024, 576); - frame.setMinimumSize(new Dimension(640, 480)); - frame.setVisible(true); - frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - - // init state - updateDestMappings(); - setSourceType(SourceType.getDefault()); - updateMatchButton(); - saveListener = null; - } - - public void setSaveListener(SaveListener val) { - saveListener = val; - } - - private void updateDestMappings() { - try { - Mappings newMappings = MappingsConverter.newMappings(classMatches, - sourceDeobfuscator.getMappings(), sourceDeobfuscator, destDeobfuscator - ); - - // look for dropped mappings - MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); - checker.dropBrokenMappings(newMappings); - - // count them - int numDroppedFields = checker.getDroppedFieldMappings().size(); - int numDroppedMethods = checker.getDroppedMethodMappings().size(); - System.out.println(String.format( - "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods", - numDroppedFields + numDroppedMethods, - numDroppedFields, - numDroppedMethods - )); - - destDeobfuscator.setMappings(newMappings); - } catch (MappingConflict ex) { - System.out.println(ex.getMessage()); - ex.printStackTrace(); - } - } - - protected void setSourceType(SourceType val) { - - // show the source classes - sourceType = val; - sourceClasses.setClasses(deobfuscateClasses(sourceType.getSourceClasses(classMatches), sourceDeobfuscator)); - - // update counts - for (SourceType sourceType : SourceType.values()) { - sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", - sourceType.name(), - sourceType.getSourceClasses(classMatches).size() - )); - } - } - - private Collection deobfuscateClasses(Collection in, Deobfuscator deobfuscator) { - List out = Lists.newArrayList(); - for (ClassEntry entry : in) { - - ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); - - // make sure we preserve any scores - if (entry instanceof ScoredClassEntry) { - deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore()); - } - - out.add(deobf); - } - return out; - } - - protected void setSourceClass(ClassEntry classEntry) { - - Runnable onGetDestClasses = null; - if (advanceCheck.isSelected()) { - onGetDestClasses = this::pickBestDestClass; - } - - setSourceClass(classEntry, onGetDestClasses); - } - - protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { - - // update the current source class - sourceClass = classEntry; - sourceClassLabel.setText(sourceClass != null ? sourceClass.getName() : ""); - - if (sourceClass != null) { - - // show the dest class(es) - ClassMatch match = classMatches.getMatchBySource(sourceDeobfuscator.obfuscateEntry(sourceClass)); - assert (match != null); - if (match.destClasses.isEmpty()) { - - destClasses.setClasses(null); - - // run in a separate thread to keep ui responsive - new Thread(() -> - { - destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); - destClasses.expandAll(); - - if (onGetDestClasses != null) { - onGetDestClasses.run(); - } - }).start(); - - } else { - - destClasses.setClasses(deobfuscateClasses(match.destClasses, destDeobfuscator)); - destClasses.expandAll(); - - if (onGetDestClasses != null) { - onGetDestClasses.run(); - } - } - } - - setDestClass(null); - sourceReader.decompileClass( - sourceClass, sourceDeobfuscator, () -> sourceReader.navigateToClassDeclaration(sourceClass)); - - updateMatchButton(); - } - - private Collection getLikelyMatches(ClassEntry sourceClass) { - - ClassEntry obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); - - // set up identifiers - ClassNamer namer = new ClassNamer(classMatches.getUniqueMatches()); - ClassIdentifier sourceIdentifier = new ClassIdentifier( - sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), - namer.getSourceNamer(), true - ); - ClassIdentifier destIdentifier = new ClassIdentifier( - destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), - namer.getDestNamer(), true - ); - - try { - - // rank all the unmatched dest classes against the source class - ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); - List scoredDestClasses = Lists.newArrayList(); - for (ClassEntry unmatchedDestClass : classMatches.getUnmatchedDestClasses()) { - ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); - float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) - / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); - scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); - } - - if (top10Matches.isSelected() && scoredDestClasses.size() > 10) { - scoredDestClasses.sort((a, b) -> - { - ScoredClassEntry sa = (ScoredClassEntry) a; - ScoredClassEntry sb = (ScoredClassEntry) b; - return -Float.compare(sa.getScore(), sb.getScore()); - }); - scoredDestClasses = scoredDestClasses.subList(0, 10); - } - - return scoredDestClasses; - - } catch (ClassNotFoundException ex) { - throw new Error("Unable to find class " + ex.getMessage()); - } - } - - protected void setDestClass(ClassEntry classEntry) { - - // update the current source class - destClass = classEntry; - destClassLabel.setText(destClass != null ? destClass.getName() : ""); - - destReader.decompileClass(destClass, destDeobfuscator, () -> destReader.navigateToClassDeclaration(destClass)); - - updateMatchButton(); - } - - private void updateMatchButton() { - - ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); - ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); - - BiMap uniqueMatches = classMatches.getUniqueMatches(); - boolean twoSelected = sourceClass != null && destClass != null; - boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); - boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest); - - GuiTricks.deactivateButton(matchButton); - if (twoSelected) { - if (isMatched) { - GuiTricks.activateButton(matchButton, "Unmatch", event -> onUnmatchClick()); - } else if (canMatch) { - GuiTricks.activateButton(matchButton, "Match", event -> onMatchClick()); - } - } - } - - private void onMatchClick() { - // precondition: source and dest classes are set correctly - - ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); - ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); - - // remove the classes from their match - classMatches.removeSource(obfSource); - classMatches.removeDest(obfDest); - - // add them as matched classes - classMatches.add(new ClassMatch(obfSource, obfDest)); - - ClassEntry nextClass = null; - if (advanceCheck.isSelected()) { - nextClass = sourceClasses.getNextClass(sourceClass); - } - - save(); - updateMatches(); - - if (nextClass != null) { - advance(nextClass); - } - } - - private void onUnmatchClick() { - // precondition: source and dest classes are set to a unique match - - ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); - - // remove the source to break the match, then add the source back as unmatched - classMatches.removeSource(obfSource); - classMatches.add(new ClassMatch(obfSource, null)); - - save(); - updateMatches(); - } - - private void updateMatches() { - updateDestMappings(); - setDestClass(null); - destClasses.setClasses(null); - updateMatchButton(); - - // remember where we were in the source tree - String packageName = sourceClasses.getSelectedPackage(); - - setSourceType(sourceType); - - sourceClasses.expandPackage(packageName); - } - - private void save() { - if (saveListener != null) { - saveListener.save(classMatches); - } - } - - private void autoMatch() { - - System.out.println("Automatching..."); - - // compute a new matching - ClassMatching matching = MappingsConverter.computeMatching( - sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), - destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), - classMatches.getUniqueMatches() - ); - ClassMatches newMatches = new ClassMatches(matching.matches()); - System.out.println(String.format("Automatch found %d new matches", - newMatches.getUniqueMatches().size() - classMatches.getUniqueMatches().size() - )); - - // update the current matches - classMatches = newMatches; - save(); - updateMatches(); - } - - private void advance() { - advance(null); - } - - private void advance(ClassEntry sourceClass) { - - // make sure we have a source class - if (sourceClass == null) { - sourceClass = sourceClasses.getSelectedClass(); - if (sourceClass != null) { - sourceClass = sourceClasses.getNextClass(sourceClass); - } else { - sourceClass = sourceClasses.getFirstClass(); - } - } - - // set the source class - setSourceClass(sourceClass, this::pickBestDestClass); - sourceClasses.setSelectionClass(sourceClass); - } - - private void pickBestDestClass() { - - // then, pick the best dest class - ClassEntry firstClass = null; - ScoredClassEntry bestDestClass = null; - for (ClassSelectorPackageNode packageNode : destClasses.packageNodes()) { - for (ClassSelectorClassNode classNode : destClasses.classNodes(packageNode)) { - if (firstClass == null) { - firstClass = classNode.getClassEntry(); - } - if (classNode.getClassEntry() instanceof ScoredClassEntry) { - ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry(); - if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { - bestDestClass = scoredClass; - } - } - } - } - - // pick the entry to show - ClassEntry destClass = null; - if (bestDestClass != null) { - destClass = bestDestClass; - } else if (firstClass != null) { - destClass = firstClass; - } - - setDestClass(destClass); - destClasses.setSelectionClass(destClass); - } - - private void toggleTop10Matches() { - if (sourceClass != null) { - destClasses.clearSelection(); - destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); - destClasses.expandAll(); - } - } - - private enum SourceType { - Matched { - @Override - public Collection getSourceClasses(ClassMatches matches) { - return matches.getUniqueMatches().keySet(); - } - }, - Unmatched { - @Override - public Collection getSourceClasses(ClassMatches matches) { - return matches.getUnmatchedSourceClasses(); - } - }, - Ambiguous { - @Override - public Collection getSourceClasses(ClassMatches matches) { - return matches.getAmbiguouslyMatchedSourceClasses(); - } - }; - - public static SourceType getDefault() { - return values()[0]; - } - - public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { - JRadioButton button = new JRadioButton(name(), this == getDefault()); - button.setActionCommand(name()); - button.addActionListener(listener); - group.add(button); - return button; - } - - public abstract Collection getSourceClasses(ClassMatches matches); - } - - public interface SaveListener { - void save(ClassMatches matches); - } -} diff --git a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java deleted file mode 100644 index fe6a3b0..0000000 --- a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java +++ /dev/null @@ -1,435 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.gui; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import cuchaz.enigma.Constants; -import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.analysis.Token; -import cuchaz.enigma.convert.ClassMatches; -import cuchaz.enigma.convert.MemberMatches; -import cuchaz.enigma.gui.highlight.DeobfuscatedHighlightPainter; -import cuchaz.enigma.gui.highlight.ObfuscatedHighlightPainter; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; -import de.sciss.syntaxpane.DefaultSyntaxKit; - -import javax.swing.*; -import javax.swing.text.Highlighter.HighlightPainter; -import java.awt.*; -import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -public class MemberMatchingGui { - - // controls - private JFrame frame; - private Map sourceTypeButtons; - private ClassSelector sourceClasses; - private CodeReader sourceReader; - private CodeReader destReader; - private JButton matchButton; - private JButton unmatchableButton; - private JLabel sourceLabel; - private JLabel destLabel; - private HighlightPainter unmatchedHighlightPainter; - private HighlightPainter matchedHighlightPainter; - private ClassMatches classMatches; - private MemberMatches memberMatches; - private Deobfuscator sourceDeobfuscator; - private Deobfuscator destDeobfuscator; - private SaveListener saveListener; - private SourceType sourceType; - private ClassEntry obfSourceClass; - private ClassEntry obfDestClass; - private T obfSourceEntry; - private T obfDestEntry; - - public MemberMatchingGui(ClassMatches classMatches, MemberMatches fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - - this.classMatches = classMatches; - memberMatches = fieldMatches; - this.sourceDeobfuscator = sourceDeobfuscator; - this.destDeobfuscator = destDeobfuscator; - - // init frame - frame = new JFrame(Constants.NAME + " - Member Matcher"); - final Container pane = frame.getContentPane(); - pane.setLayout(new BorderLayout()); - - // init classes side - JPanel classesPanel = new JPanel(); - classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); - classesPanel.setPreferredSize(new Dimension(200, 0)); - pane.add(classesPanel, BorderLayout.WEST); - classesPanel.add(new JLabel("Classes")); - - // init source type radios - JPanel sourceTypePanel = new JPanel(); - classesPanel.add(sourceTypePanel); - sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); - ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); - ButtonGroup sourceTypeButtons = new ButtonGroup(); - this.sourceTypeButtons = Maps.newHashMap(); - for (SourceType sourceType : SourceType.values()) { - JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); - this.sourceTypeButtons.put(sourceType, button); - sourceTypePanel.add(button); - } - - sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); - sourceClasses.setSelectionListener(this::setSourceClass); - JScrollPane sourceScroller = new JScrollPane(sourceClasses); - classesPanel.add(sourceScroller); - - // init readers - DefaultSyntaxKit.initKit(); - sourceReader = new CodeReader(); - sourceReader.setSelectionListener(reference -> - { - if (reference != null) { - onSelectSource(reference.entry); - } else { - onSelectSource(null); - } - }); - destReader = new CodeReader(); - destReader.setSelectionListener(reference -> - { - if (reference != null) { - onSelectDest(reference.entry); - } else { - onSelectDest(null); - } - }); - - // add key bindings - KeyAdapter keyListener = new KeyAdapter() { - @Override - public void keyPressed(KeyEvent event) { - if (event.getKeyCode() == KeyEvent.VK_M) - matchButton.doClick(); - } - }; - sourceReader.addKeyListener(keyListener); - destReader.addKeyListener(keyListener); - - // init all the splits - JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(sourceReader), new JScrollPane( - destReader)); - splitRight.setResizeWeight(0.5); // resize 50:50 - JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); - splitLeft.setResizeWeight(0); // let the right side take all the slack - pane.add(splitLeft, BorderLayout.CENTER); - splitLeft.resetToPreferredSizes(); - - // init bottom panel - JPanel bottomPanel = new JPanel(); - bottomPanel.setLayout(new FlowLayout()); - pane.add(bottomPanel, BorderLayout.SOUTH); - - matchButton = new JButton(); - unmatchableButton = new JButton(); - - sourceLabel = new JLabel(); - bottomPanel.add(sourceLabel); - bottomPanel.add(matchButton); - bottomPanel.add(unmatchableButton); - destLabel = new JLabel(); - bottomPanel.add(destLabel); - - // show the frame - pane.doLayout(); - frame.setSize(1024, 576); - frame.setMinimumSize(new Dimension(640, 480)); - frame.setVisible(true); - frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - - unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); - matchedHighlightPainter = new DeobfuscatedHighlightPainter(); - - // init state - saveListener = null; - obfSourceClass = null; - obfDestClass = null; - obfSourceEntry = null; - obfDestEntry = null; - setSourceType(SourceType.getDefault()); - updateButtons(); - } - - protected void setSourceType(SourceType val) { - sourceType = val; - updateSourceClasses(); - } - - public void setSaveListener(SaveListener val) { - saveListener = val; - } - - private void updateSourceClasses() { - - String selectedPackage = sourceClasses.getSelectedPackage(); - - List deobfClassEntries = Lists.newArrayList(); - for (ClassEntry entry : sourceType.getObfSourceClasses(memberMatches)) { - deobfClassEntries.add(sourceDeobfuscator.deobfuscateEntry(entry)); - } - sourceClasses.setClasses(deobfClassEntries); - - if (selectedPackage != null) { - sourceClasses.expandPackage(selectedPackage); - } - - for (SourceType sourceType : SourceType.values()) { - sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", - sourceType.name(), sourceType.getObfSourceClasses(memberMatches).size() - )); - } - } - - protected void setSourceClass(ClassEntry sourceClass) { - - obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); - obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); - if (obfDestClass == null) { - throw new Error("No matching dest class for source class: " + obfSourceClass); - } - - sourceReader.decompileClass(obfSourceClass, sourceDeobfuscator, false, this::updateSourceHighlights); - destReader.decompileClass(obfDestClass, destDeobfuscator, false, this::updateDestHighlights); - } - - protected void updateSourceHighlights() { - highlightEntries(sourceReader, sourceDeobfuscator, memberMatches.matches().keySet(), memberMatches.getUnmatchedSourceEntries()); - } - - protected void updateDestHighlights() { - highlightEntries(destReader, destDeobfuscator, memberMatches.matches().values(), memberMatches.getUnmatchedDestEntries()); - } - - private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection obfMatchedEntries, Collection obfUnmatchedEntries) { - reader.clearHighlights(); - // matched fields - updateHighlighted(obfMatchedEntries, deobfuscator, reader, matchedHighlightPainter); - // unmatched fields - updateHighlighted(obfUnmatchedEntries, deobfuscator, reader, unmatchedHighlightPainter); - } - - private void updateHighlighted(Collection entries, Deobfuscator deobfuscator, CodeReader reader, HighlightPainter painter) { - SourceIndex index = reader.getSourceIndex(); - for (T obfT : entries) { - T deobfT = deobfuscator.deobfuscateEntry(obfT); - Token token = index.getDeclarationToken(deobfT); - if (token != null) { - reader.setHighlightedToken(token, painter); - } - } - } - - private boolean isSelectionMatched() { - return obfSourceEntry != null && obfDestEntry != null - && memberMatches.isMatched(obfSourceEntry, obfDestEntry); - } - - protected void onSelectSource(Entry source) { - - // start with no selection - if (isSelectionMatched()) { - setDest(null); - } - setSource(null); - - // then look for a valid source selection - if (source != null) { - - // this looks really scary, but it's actually ok - // Deobfuscator.obfuscateEntry can handle all implementations of Entry - // and MemberMatches.hasSource() will only pass entries that actually match T - @SuppressWarnings("unchecked") - T sourceEntry = (T) source; - - T obfSourceEntry = sourceDeobfuscator.obfuscateEntry(sourceEntry); - if (memberMatches.hasSource(obfSourceEntry)) { - setSource(obfSourceEntry); - - // look for a matched dest too - T obfDestEntry = memberMatches.matches().get(obfSourceEntry); - if (obfDestEntry != null) { - setDest(obfDestEntry); - } - } - } - - updateButtons(); - } - - protected void onSelectDest(Entry dest) { - - // start with no selection - if (isSelectionMatched()) { - setSource(null); - } - setDest(null); - - // then look for a valid dest selection - if (dest != null) { - - // this looks really scary, but it's actually ok - // Deobfuscator.obfuscateEntry can handle all implementations of Entry - // and MemberMatches.hasSource() will only pass entries that actually match T - @SuppressWarnings("unchecked") - T destEntry = (T) dest; - - T obfDestEntry = destDeobfuscator.obfuscateEntry(destEntry); - if (memberMatches.hasDest(obfDestEntry)) { - setDest(obfDestEntry); - - // look for a matched source too - T obfSourceEntry = memberMatches.matches().inverse().get(obfDestEntry); - if (obfSourceEntry != null) { - setSource(obfSourceEntry); - } - } - } - - updateButtons(); - } - - private void setSource(T obfEntry) { - if (obfEntry == null) { - obfSourceEntry = null; - sourceLabel.setText(""); - } else { - obfSourceEntry = obfEntry; - sourceLabel.setText(getEntryLabel(obfEntry, sourceDeobfuscator)); - } - } - - private void setDest(T obfEntry) { - if (obfEntry == null) { - obfDestEntry = null; - destLabel.setText(""); - } else { - obfDestEntry = obfEntry; - destLabel.setText(getEntryLabel(obfEntry, destDeobfuscator)); - } - } - - private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) { - // show obfuscated and deobfuscated names, but no types/signatures - T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry); - return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName()); - } - - private void updateButtons() { - - GuiTricks.deactivateButton(matchButton); - GuiTricks.deactivateButton(unmatchableButton); - - if (obfSourceEntry != null && obfDestEntry != null) { - if (memberMatches.isMatched(obfSourceEntry, obfDestEntry)) - GuiTricks.activateButton(matchButton, "Unmatch", event -> unmatch()); - else if (!memberMatches.isMatchedSourceEntry(obfSourceEntry) && !memberMatches.isMatchedDestEntry( - obfDestEntry)) - GuiTricks.activateButton(matchButton, "Match", event -> match()); - } else if (obfSourceEntry != null) - GuiTricks.activateButton(unmatchableButton, "Set Unmatchable", event -> unmatchable()); - } - - protected void match() { - - // update the field matches - memberMatches.makeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); - save(); - - // update the ui - onSelectSource(null); - onSelectDest(null); - updateSourceHighlights(); - updateDestHighlights(); - updateSourceClasses(); - } - - protected void unmatch() { - - // update the field matches - memberMatches.unmakeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); - save(); - - // update the ui - onSelectSource(null); - onSelectDest(null); - updateSourceHighlights(); - updateDestHighlights(); - updateSourceClasses(); - } - - protected void unmatchable() { - - // update the field matches - memberMatches.makeSourceUnmatchable(obfSourceEntry, sourceDeobfuscator); - save(); - - // update the ui - onSelectSource(null); - onSelectDest(null); - updateSourceHighlights(); - updateDestHighlights(); - updateSourceClasses(); - } - - private void save() { - if (saveListener != null) { - saveListener.save(memberMatches); - } - } - - private enum SourceType { - Matched { - @Override - public Collection getObfSourceClasses(MemberMatches matches) { - return matches.getSourceClassesWithoutUnmatchedEntries(); - } - }, - Unmatched { - @Override - public Collection getObfSourceClasses(MemberMatches matches) { - return matches.getSourceClassesWithUnmatchedEntries(); - } - }; - - public static SourceType getDefault() { - return values()[0]; - } - - public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { - JRadioButton button = new JRadioButton(name(), this == getDefault()); - button.setActionCommand(name()); - button.addActionListener(listener); - group.add(button); - return button; - } - - public abstract Collection getObfSourceClasses(MemberMatches matches); - } - - public interface SaveListener { - void save(MemberMatches matches); - } -} -- cgit v1.2.3