summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar lclc982016-07-04 18:14:22 +1000
committerGravatar lclc982016-07-04 18:14:22 +1000
commit59e189bef2b5e6d129fb7c2c988ed0b2130e36ac (patch)
tree2b638e60905251de85a4917152d6fc39a4112194
parentFixed Obf Class list order (diff)
downloadenigma-59e189bef2b5e6d129fb7c2c988ed0b2130e36ac.tar.gz
enigma-59e189bef2b5e6d129fb7c2c988ed0b2130e36ac.tar.xz
enigma-59e189bef2b5e6d129fb7c2c988ed0b2130e36ac.zip
Reformat
-rw-r--r--src/main/java/cuchaz/enigma/CommandMain.java43
-rw-r--r--src/main/java/cuchaz/enigma/ConvertMain.java348
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java120
-rw-r--r--src/main/java/cuchaz/enigma/MainFormatConverter.java113
-rw-r--r--src/main/java/cuchaz/enigma/TranslatingTypeLoader.java76
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java4
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java52
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarClassIterator.java8
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java8
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java4
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Token.java4
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java41
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/CheckCastIterator.java113
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java26
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/InfoType.java62
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java47
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassForest.java54
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassIdentifier.java54
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassIdentity.java441
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassMatch.java84
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassMatches.java149
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassMatching.java146
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassNamer.java56
-rw-r--r--src/main/java/cuchaz/enigma/convert/MappingsConverter.java540
-rw-r--r--src/main/java/cuchaz/enigma/convert/MatchesReader.java104
-rw-r--r--src/main/java/cuchaz/enigma/convert/MatchesWriter.java121
-rw-r--r--src/main/java/cuchaz/enigma/convert/MemberMatches.java143
-rw-r--r--src/main/java/cuchaz/enigma/gui/BrowserCaret.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java521
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassSelector.java143
-rw-r--r--src/main/java/cuchaz/enigma/gui/CodeReader.java202
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java20
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiTricks.java52
-rw-r--r--src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java442
-rw-r--r--src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java29
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java6
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/elements/MenuBar.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/BoxHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/OtherHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/SelectionHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java (renamed from src/main/java/cuchaz/enigma/gui/ClassSelectorClassNode.java)7
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java (renamed from src/main/java/cuchaz/enigma/gui/ClassSelectorPackageNode.java)8
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java1
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelObf.java2
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java56
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java27
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassEntry.java45
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java46
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java45
-rw-r--r--src/main/java/cuchaz/enigma/mapping/EntryFactory.java36
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldEntry.java46
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java42
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java83
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsChecker.java44
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsReader.java41
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsReaderOld.java5
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java26
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsWriter.java15
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MemberMapping.java2
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodEntry.java46
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java93
-rw-r--r--src/main/java/cuchaz/enigma/mapping/NameValidator.java1
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java22
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Signature.java50
-rw-r--r--src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java82
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java48
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Type.java62
-rw-r--r--src/main/java/cuchaz/enigma/throwables/IllegalNameException.java (renamed from src/main/java/cuchaz/enigma/mapping/IllegalNameException.java)24
-rw-r--r--src/main/java/cuchaz/enigma/throwables/MappingConflict.java7
-rw-r--r--src/main/java/cuchaz/enigma/throwables/MappingParseException.java (renamed from src/main/java/cuchaz/enigma/mapping/MappingParseException.java)4
-rw-r--r--src/main/java/cuchaz/enigma/utils/ReadableToken.java (renamed from src/main/java/cuchaz/enigma/gui/ReadableToken.java)2
-rw-r--r--src/main/java/cuchaz/enigma/utils/Utils.java134
85 files changed, 682 insertions, 4892 deletions
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java
index 8dfbe24d..d7967b46 100644
--- a/src/main/java/cuchaz/enigma/CommandMain.java
+++ b/src/main/java/cuchaz/enigma/CommandMain.java
@@ -23,42 +23,38 @@ public class CommandMain {
23 23
24 private static final int ReportTime = 5000; // 5s 24 private static final int ReportTime = 5000; // 5s
25 25
26 private int m_totalWork; 26 private int totalWork;
27 private long m_startTime; 27 private long startTime;
28 private long m_lastReportTime; 28 private long lastReportTime;
29 29
30 @Override 30 @Override
31 public void init(int totalWork, String title) { 31 public void init(int totalWork, String title) {
32 m_totalWork = totalWork; 32 this.totalWork = totalWork;
33 m_startTime = System.currentTimeMillis(); 33 this.startTime = System.currentTimeMillis();
34 m_lastReportTime = m_startTime; 34 this.lastReportTime = this.startTime;
35 System.out.println(title); 35 System.out.println(title);
36 } 36 }
37 37
38 @Override 38 @Override
39 public void onProgress(int numDone, String message) { 39 public void onProgress(int numDone, String message) {
40
41 long now = System.currentTimeMillis(); 40 long now = System.currentTimeMillis();
42 boolean isLastUpdate = numDone == m_totalWork; 41 boolean isLastUpdate = numDone == this.totalWork;
43 boolean shouldReport = isLastUpdate || now - m_lastReportTime > ReportTime; 42 boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime;
44 43
45 if (shouldReport) { 44 if (shouldReport) {
46 int percent = numDone * 100 / m_totalWork; 45 int percent = numDone * 100 / this.totalWork;
47 System.out.println(String.format("\tProgress: %3d%%", percent)); 46 System.out.println(String.format("\tProgress: %3d%%", percent));
48 m_lastReportTime = now; 47 this.lastReportTime = now;
49 } 48 }
50 if (isLastUpdate) { 49 if (isLastUpdate) {
51 double elapsedSeconds = (now - m_startTime) / 1000; 50 double elapsedSeconds = (now - this.startTime) / 1000;
52 System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); 51 System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds));
53 } 52 }
54 } 53 }
55 } 54 }
56 55
57 public static void main(String[] args) 56 public static void main(String[] args) throws Exception {
58 throws Exception {
59
60 try { 57 try {
61
62 // process the command 58 // process the command
63 String command = getArg(args, 0, "command", true); 59 String command = getArg(args, 0, "command", true);
64 if (command.equalsIgnoreCase("deobfuscate")) { 60 if (command.equalsIgnoreCase("deobfuscate")) {
@@ -88,8 +84,7 @@ public class CommandMain {
88 System.out.println("\t\tprotectify <in jar> <out jar>"); 84 System.out.println("\t\tprotectify <in jar> <out jar>");
89 } 85 }
90 86
91 private static void decompile(String[] args) 87 private static void decompile(String[] args) throws Exception {
92 throws Exception {
93 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 88 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
94 File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); 89 File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true));
95 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); 90 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false));
@@ -97,8 +92,7 @@ public class CommandMain {
97 deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); 92 deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener());
98 } 93 }
99 94
100 private static void deobfuscate(String[] args) 95 private static void deobfuscate(String[] args) throws Exception {
101 throws Exception {
102 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 96 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
103 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); 97 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true));
104 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); 98 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false));
@@ -106,24 +100,21 @@ public class CommandMain {
106 deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); 100 deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener());
107 } 101 }
108 102
109 private static void protectify(String[] args) 103 private static void protectify(String[] args) throws Exception {
110 throws Exception {
111 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 104 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
112 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); 105 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true));
113 Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); 106 Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn));
114 deobfuscator.protectifyJar(fileJarOut, new ConsoleProgressListener()); 107 deobfuscator.protectifyJar(fileJarOut, new ConsoleProgressListener());
115 } 108 }
116 109
117 private static void publify(String[] args) 110 private static void publify(String[] args) throws Exception {
118 throws Exception {
119 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 111 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
120 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); 112 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true));
121 Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); 113 Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn));
122 deobfuscator.publifyJar(fileJarOut, new ConsoleProgressListener()); 114 deobfuscator.publifyJar(fileJarOut, new ConsoleProgressListener());
123 } 115 }
124 116
125 private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) 117 private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception {
126 throws Exception {
127 System.out.println("Reading jar..."); 118 System.out.println("Reading jar...");
128 Deobfuscator deobfuscator = new Deobfuscator(jar); 119 Deobfuscator deobfuscator = new Deobfuscator(jar);
129 if (fileMappings != null) { 120 if (fileMappings != null) {
diff --git a/src/main/java/cuchaz/enigma/ConvertMain.java b/src/main/java/cuchaz/enigma/ConvertMain.java
deleted file mode 100644
index caa61545..00000000
--- a/src/main/java/cuchaz/enigma/ConvertMain.java
+++ /dev/null
@@ -1,348 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma;
12
13import java.io.File;
14import java.io.IOException;
15import java.util.jar.JarFile;
16
17import cuchaz.enigma.convert.*;
18import cuchaz.enigma.gui.ClassMatchingGui;
19import cuchaz.enigma.gui.MemberMatchingGui;
20import cuchaz.enigma.mapping.*;
21
22
23public class ConvertMain {
24
25 public static void main(String[] args)
26 throws IOException, MappingParseException {
27 try {
28 //Get all are args
29 String JarOld = getArg(args, 1, "Path to Old Jar", true);
30 String JarNew = getArg(args, 2, "Path to New Jar", true);
31 String OldMappings = getArg(args, 3, "Path to old .mappings file", true);
32 String NewMappings = getArg(args, 4, "Path to new .mappings file", true);
33 String ClassMatches = getArg(args, 5, "Path to Class .matches file", true);
34 String FieldMatches = getArg(args, 6, "Path to Field .matches file", true);
35 String MethodMatches = getArg(args, 7, "Path to Method .matches file", true);
36 //OldJar
37 JarFile sourceJar = new JarFile(new File(JarOld));
38 //NewJar
39 JarFile destJar = new JarFile(new File(JarNew));
40 //Get the mapping files
41 File inMappingsFile = new File(OldMappings);
42 File outMappingsFile = new File(NewMappings);
43 Mappings mappings = new MappingsReader().read(inMappingsFile);
44 //Make the Match Files..
45 File classMatchesFile = new File(ClassMatches);
46 File fieldMatchesFile = new File(FieldMatches);
47 File methodMatchesFile = new File(MethodMatches);
48
49 String command = getArg(args, 0, "command", true);
50
51 if (command.equalsIgnoreCase("computeClassMatches")) {
52 computeClassMatches(classMatchesFile, sourceJar, destJar, mappings);
53 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile);
54 } else if (command.equalsIgnoreCase("editClassMatches")) {
55 editClasssMatches(classMatchesFile, sourceJar, destJar, mappings);
56 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile);
57 } else if (command.equalsIgnoreCase("computeFieldMatches")) {
58 computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile);
59 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile);
60 } else if (command.equalsIgnoreCase("editFieldMatches")) {
61 editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile);
62 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile);
63 } else if (command.equalsIgnoreCase("computeMethodMatches")) {
64 computeMethodMatches(methodMatchesFile, destJar, outMappingsFile, classMatchesFile);
65 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile);
66 } else if (command.equalsIgnoreCase("editMethodMatches")) {
67 editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile);
68 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile);
69 } else if (command.equalsIgnoreCase("convertMappings")) {
70 convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile);
71 }
72 } catch (IllegalArgumentException ex) {
73 System.out.println(ex.getMessage());
74 printHelp();
75 }
76 }
77
78 private static void printHelp() {
79 System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION));
80 System.out.println("Usage:");
81 System.out.println("\tjava -cp enigma.jar cuchaz.enigma.ConvertMain <command> <old-jar> <new-jar> <old-mappings> <new-mappings> <class-matches> <field-matches> <method-matches>");
82 System.out.println("\tWhere <command> is one of:");
83 System.out.println("\t\tcomputeClassMatches");
84 System.out.println("\t\teditClassMatches");
85 System.out.println("\t\tcomputeFieldMatches");
86 System.out.println("\t\teditFieldMatches");
87 System.out.println("\t\teditMethodMatches");
88 System.out.println("\t\tconvertMappings");
89 System.out.println("\tWhere <old-jar> is the already mapped jar.");
90 System.out.println("\tWhere <new-jar> is the unmapped jar.");
91 System.out.println("\tWhere <old-mappings> is the path to the mappings for the old jar.");
92 System.out.println("\tWhere <new-mappings> is the new mappings. (Where you want to save them and there name)");
93 System.out.println("\tWhere <class-matches> is the class matches file.");
94 System.out.println("\tWhere <field-matches> is the field matches file.");
95 System.out.println("\tWhere <method-matches> is the method matches file.");
96 }
97
98 //Copy of getArg from CommandMain.... Should make a utils class.
99 private static String getArg(String[] args, int i, String name, boolean required) {
100 if (i >= args.length) {
101 if (required) {
102 throw new IllegalArgumentException(name + " is required");
103 } else {
104 return null;
105 }
106 }
107 return args[i];
108 }
109
110 private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings)
111 throws IOException {
112 ClassMatches classMatches = MappingsConverter.computeClassMatches(sourceJar, destJar, mappings);
113 MatchesWriter.writeClasses(classMatches, classMatchesFile);
114 System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath());
115 }
116
117 private static void editClasssMatches(final File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings)
118 throws IOException {
119 System.out.println("Reading class matches...");
120 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
121 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
122 deobfuscators.source.setMappings(mappings);
123 System.out.println("Starting GUI...");
124 new ClassMatchingGui(classMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(matches -> {
125 try {
126 MatchesWriter.writeClasses(matches, classMatchesFile);
127 } catch (IOException ex) {
128 throw new Error(ex);
129 }
130 });
131 }
132
133 @SuppressWarnings("unused")
134 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile)
135 throws IOException {
136 System.out.println("Reading class matches...");
137 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
138 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
139 deobfuscators.source.setMappings(mappings);
140
141 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest);
142 new MappingsWriter().write(outMappingsFile, newMappings);
143 System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath());
144 }
145
146 private static void computeFieldMatches(File memberMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile)
147 throws IOException, MappingParseException {
148
149 System.out.println("Reading class matches...");
150 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
151 System.out.println("Reading mappings...");
152 Mappings destMappings = new MappingsReader().read(destMappingsFile);
153 System.out.println("Indexing dest jar...");
154 Deobfuscator destDeobfuscator = new Deobfuscator(destJar);
155
156 System.out.println("Writing matches...");
157
158 // get the matched and unmatched mappings
159 MemberMatches<FieldEntry> fieldMatches = MappingsConverter.computeMemberMatches(
160 destDeobfuscator,
161 destMappings,
162 classMatches,
163 MappingsConverter.getFieldDoer()
164 );
165
166 MatchesWriter.writeMembers(fieldMatches, memberMatchesFile);
167 System.out.println("Wrote:\n\t" + memberMatchesFile.getAbsolutePath());
168 }
169
170 private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile)
171 throws IOException, MappingParseException {
172
173 System.out.println("Reading matches...");
174 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
175 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile);
176
177 // prep deobfuscators
178 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
179 deobfuscators.source.setMappings(sourceMappings);
180 Mappings destMappings = new MappingsReader().read(destMappingsFile);
181 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex());
182 checker.dropBrokenMappings(destMappings);
183 deobfuscators.dest.setMappings(destMappings);
184
185 new MemberMatchingGui<>(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(matches -> {
186 try {
187 MatchesWriter.writeMembers(matches, fieldMatchesFile);
188 } catch (IOException ex) {
189 throw new Error(ex);
190 }
191 });
192 }
193
194 @SuppressWarnings("unused")
195 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile)
196 throws IOException {
197
198 System.out.println("Reading matches...");
199 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
200 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile);
201
202 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
203 deobfuscators.source.setMappings(mappings);
204
205 // apply matches
206 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest);
207 MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer());
208
209 // write out the converted mappings
210
211 new MappingsWriter().write(outMappingsFile, newMappings);
212 System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath());
213 }
214
215
216 private static void computeMethodMatches(File methodMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile)
217 throws IOException, MappingParseException {
218
219 System.out.println("Reading class matches...");
220 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
221 System.out.println("Reading mappings...");
222 Mappings destMappings = new MappingsReader().read(destMappingsFile);
223 System.out.println("Indexing dest jar...");
224 Deobfuscator destDeobfuscator = new Deobfuscator(destJar);
225
226 System.out.println("Writing method matches...");
227
228 // get the matched and unmatched mappings
229 MemberMatches<BehaviorEntry> methodMatches = MappingsConverter.computeMemberMatches(
230 destDeobfuscator,
231 destMappings,
232 classMatches,
233 MappingsConverter.getMethodDoer()
234 );
235
236 MatchesWriter.writeMembers(methodMatches, methodMatchesFile);
237 System.out.println("Wrote:\n\t" + methodMatchesFile.getAbsolutePath());
238 }
239
240 private static void editMethodMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File methodMatchesFile)
241 throws IOException, MappingParseException {
242
243 System.out.println("Reading matches...");
244 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
245 MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile);
246
247 // prep deobfuscators
248 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
249 deobfuscators.source.setMappings(sourceMappings);
250 Mappings destMappings = new MappingsReader().read(destMappingsFile);
251 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex());
252 checker.dropBrokenMappings(destMappings);
253 deobfuscators.dest.setMappings(destMappings);
254
255 new MemberMatchingGui<>(classMatches, methodMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(matches -> {
256 try {
257 MatchesWriter.writeMembers(matches, methodMatchesFile);
258 } catch (IOException ex) {
259 throw new Error(ex);
260 }
261 });
262 }
263
264 private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile)
265 throws IOException {
266
267 System.out.println("Reading matches...");
268 ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile);
269 MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile);
270 MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile);
271
272 Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar);
273 deobfuscators.source.setMappings(mappings);
274
275 // apply matches
276 Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest);
277 MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer());
278 MappingsConverter.applyMemberMatches(newMappings, classMatches, methodMatches, MappingsConverter.getMethodDoer());
279
280 // check the final mappings
281 MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex());
282 checker.dropBrokenMappings(newMappings);
283
284 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
285 System.out.println("WARNING: Broken class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")");
286 }
287 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) {
288 System.out.println("WARNING: Broken inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")");
289 }
290 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) {
291 System.out.println("WARNING: Broken field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")");
292 }
293 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) {
294 System.out.println("WARNING: Broken behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")");
295 }
296
297 // write out the converted mappings
298 new MappingsWriter().write(outMappingsFile, newMappings);
299 System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath());
300 }
301
302 private static class Deobfuscators {
303
304 public Deobfuscator source;
305 public Deobfuscator dest;
306
307 public Deobfuscators(JarFile sourceJar, JarFile destJar) {
308 System.out.println("Indexing source jar...");
309 IndexerThread sourceIndexer = new IndexerThread(sourceJar);
310 sourceIndexer.start();
311 System.out.println("Indexing dest jar...");
312 IndexerThread destIndexer = new IndexerThread(destJar);
313 destIndexer.start();
314 sourceIndexer.joinOrBail();
315 destIndexer.joinOrBail();
316 source = sourceIndexer.deobfuscator;
317 dest = destIndexer.deobfuscator;
318 }
319 }
320
321 private static class IndexerThread extends Thread {
322
323 private JarFile m_jarFile;
324 public Deobfuscator deobfuscator;
325
326 public IndexerThread(JarFile jarFile) {
327 m_jarFile = jarFile;
328 deobfuscator = null;
329 }
330
331 public void joinOrBail() {
332 try {
333 join();
334 } catch (InterruptedException ex) {
335 throw new Error(ex);
336 }
337 }
338
339 @Override
340 public void run() {
341 try {
342 deobfuscator = new Deobfuscator(m_jarFile);
343 } catch (IOException ex) {
344 throw new Error(ex);
345 }
346 }
347 }
348} \ No newline at end of file
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index f917deb7..d22260fb 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -47,50 +47,46 @@ public class Deobfuscator {
47 void onProgress(int numDone, String message); 47 void onProgress(int numDone, String message);
48 } 48 }
49 49
50 private JarFile m_jar; 50 private JarFile jar;
51 private DecompilerSettings m_settings; 51 private DecompilerSettings settings;
52 private JarIndex m_jarIndex; 52 private JarIndex jarIndex;
53 private Mappings m_mappings; 53 private Mappings mappings;
54 private MappingsRenamer m_renamer; 54 private MappingsRenamer renamer;
55 private Map<TranslationDirection, Translator> m_translatorCache; 55 private Map<TranslationDirection, Translator> translatorCache;
56 56
57 public Deobfuscator(JarFile jar) throws IOException { 57 public Deobfuscator(JarFile jar) {
58 m_jar = jar; 58 this.jar = jar;
59 59
60 // build the jar index 60 // build the jar index
61 m_jarIndex = new JarIndex(); 61 this.jarIndex = new JarIndex();
62 m_jarIndex.indexJar(m_jar, true); 62 this.jarIndex.indexJar(this.jar, true);
63 63
64 // config the decompiler 64 // config the decompiler
65 m_settings = DecompilerSettings.javaDefaults(); 65 this.settings = DecompilerSettings.javaDefaults();
66 m_settings.setMergeVariables(true); 66 this.settings.setMergeVariables(true);
67 m_settings.setForceExplicitImports(true); 67 this.settings.setForceExplicitImports(true);
68 m_settings.setForceExplicitTypeArguments(true); 68 this.settings.setForceExplicitTypeArguments(true);
69 m_settings.setShowDebugLineNumbers(true); 69 this.settings.setShowDebugLineNumbers(true);
70 // DEBUG 70 // DEBUG
71 //m_settings.setShowSyntheticMembers(true); 71 //this.settings.setShowSyntheticMembers(true);
72 72
73 // init defaults 73 // init defaults
74 m_translatorCache = Maps.newTreeMap(); 74 this.translatorCache = Maps.newTreeMap();
75 75
76 // init mappings 76 // init mappings
77 setMappings(new Mappings()); 77 setMappings(new Mappings());
78 } 78 }
79 79
80 public JarFile getJar() {
81 return m_jar;
82 }
83
84 public String getJarName() { 80 public String getJarName() {
85 return m_jar.getName(); 81 return this.jar.getName();
86 } 82 }
87 83
88 public JarIndex getJarIndex() { 84 public JarIndex getJarIndex() {
89 return m_jarIndex; 85 return this.jarIndex;
90 } 86 }
91 87
92 public Mappings getMappings() { 88 public Mappings getMappings() {
93 return m_mappings; 89 return this.mappings;
94 } 90 }
95 91
96 public void setMappings(Mappings val) { 92 public void setMappings(Mappings val) {
@@ -103,7 +99,7 @@ public class Deobfuscator {
103 } 99 }
104 100
105 // drop mappings that don't match the jar 101 // drop mappings that don't match the jar
106 MappingsChecker checker = new MappingsChecker(m_jarIndex); 102 MappingsChecker checker = new MappingsChecker(this.jarIndex);
107 checker.dropBrokenMappings(val); 103 checker.dropBrokenMappings(val);
108 if (warnAboutDrops) { 104 if (warnAboutDrops) {
109 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { 105 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
@@ -120,22 +116,22 @@ public class Deobfuscator {
120 } 116 }
121 } 117 }
122 118
123 m_mappings = val; 119 this.mappings = val;
124 m_renamer = new MappingsRenamer(m_jarIndex, val); 120 this.renamer = new MappingsRenamer(this.jarIndex, val);
125 m_translatorCache.clear(); 121 this.translatorCache.clear();
126 } 122 }
127 123
128 public Translator getTranslator(TranslationDirection direction) { 124 public Translator getTranslator(TranslationDirection direction) {
129 Translator translator = m_translatorCache.get(direction); 125 Translator translator = this.translatorCache.get(direction);
130 if (translator == null) { 126 if (translator == null) {
131 translator = m_mappings.getTranslator(direction, m_jarIndex.getTranslationIndex()); 127 translator = this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex());
132 m_translatorCache.put(direction, translator); 128 this.translatorCache.put(direction, translator);
133 } 129 }
134 return translator; 130 return translator;
135 } 131 }
136 132
137 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { 133 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) {
138 for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) { 134 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) {
139 // skip inner classes 135 // skip inner classes
140 if (obfClassEntry.isInnerClass()) { 136 if (obfClassEntry.isInnerClass()) {
141 continue; 137 continue;
@@ -166,19 +162,19 @@ public class Deobfuscator {
166 String deobfClassName = className; 162 String deobfClassName = className;
167 163
168 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name 164 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name
169 ClassMapping classMapping = m_mappings.getClassByObf(className); 165 ClassMapping classMapping = this.mappings.getClassByObf(className);
170 if (classMapping != null && classMapping.getDeobfName() != null) { 166 if (classMapping != null && classMapping.getDeobfName() != null) {
171 deobfClassName = classMapping.getDeobfName(); 167 deobfClassName = classMapping.getDeobfName();
172 } 168 }
173 169
174 // set the type loader 170 // set the type loader
175 TranslatingTypeLoader loader = new TranslatingTypeLoader( 171 TranslatingTypeLoader loader = new TranslatingTypeLoader(
176 m_jar, 172 this.jar,
177 m_jarIndex, 173 this.jarIndex,
178 getTranslator(TranslationDirection.Obfuscating), 174 getTranslator(TranslationDirection.Obfuscating),
179 getTranslator(TranslationDirection.Deobfuscating) 175 getTranslator(TranslationDirection.Deobfuscating)
180 ); 176 );
181 m_settings.setTypeLoader(loader); 177 this.settings.setTypeLoader(loader);
182 178
183 // see if procyon can find the type 179 // see if procyon can find the type
184 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); 180 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName);
@@ -192,7 +188,7 @@ public class Deobfuscator {
192 // decompile it! 188 // decompile it!
193 DecompilerContext context = new DecompilerContext(); 189 DecompilerContext context = new DecompilerContext();
194 context.setCurrentType(resolvedType); 190 context.setCurrentType(resolvedType);
195 context.setSettings(m_settings); 191 context.setSettings(this.settings);
196 AstBuilder builder = new AstBuilder(context); 192 AstBuilder builder = new AstBuilder(context);
197 builder.addType(resolvedType); 193 builder.addType(resolvedType);
198 builder.runTransformations(null); 194 builder.runTransformations(null);
@@ -225,7 +221,7 @@ public class Deobfuscator {
225 Entry obfEntry = obfuscateEntry(deobfReference.entry); 221 Entry obfEntry = obfuscateEntry(deobfReference.entry);
226 222
227 // try to resolve the class 223 // try to resolve the class
228 ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); 224 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry);
229 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { 225 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) {
230 // change the class of the entry 226 // change the class of the entry
231 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); 227 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry);
@@ -246,14 +242,14 @@ public class Deobfuscator {
246 // render the AST into source 242 // render the AST into source
247 StringWriter buf = new StringWriter(); 243 StringWriter buf = new StringWriter();
248 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); 244 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null);
249 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), m_settings), null); 245 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null);
250 return buf.toString(); 246 return buf.toString();
251 } 247 }
252 248
253 public void writeSources(File dirOut, ProgressListener progress) throws IOException { 249 public void writeSources(File dirOut, ProgressListener progress) {
254 // get the classes to decompile 250 // get the classes to decompile
255 Set<ClassEntry> classEntries = Sets.newHashSet(); 251 Set<ClassEntry> classEntries = Sets.newHashSet();
256 for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) { 252 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) {
257 // skip inner classes 253 // skip inner classes
258 if (obfClassEntry.isInnerClass()) { 254 if (obfClassEntry.isInnerClass()) {
259 continue; 255 continue;
@@ -298,8 +294,8 @@ public class Deobfuscator {
298 294
299 public void writeJar(File out, ProgressListener progress) { 295 public void writeJar(File out, ProgressListener progress) {
300 final TranslatingTypeLoader loader = new TranslatingTypeLoader( 296 final TranslatingTypeLoader loader = new TranslatingTypeLoader(
301 m_jar, 297 this.jar,
302 m_jarIndex, 298 this.jarIndex,
303 getTranslator(TranslationDirection.Obfuscating), 299 getTranslator(TranslationDirection.Obfuscating),
304 getTranslator(TranslationDirection.Deobfuscating) 300 getTranslator(TranslationDirection.Deobfuscating)
305 ); 301 );
@@ -321,11 +317,11 @@ public class Deobfuscator {
321 private void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { 317 private void transformJar(File out, ProgressListener progress, ClassTransformer transformer) {
322 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { 318 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) {
323 if (progress != null) { 319 if (progress != null) {
324 progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Transforming classes..."); 320 progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes...");
325 } 321 }
326 322
327 int i = 0; 323 int i = 0;
328 for (CtClass c : JarClassIterator.classes(m_jar)) { 324 for (CtClass c : JarClassIterator.classes(this.jar)) {
329 if (progress != null) { 325 if (progress != null) {
330 progress.onProgress(i++, c.getName()); 326 progress.onProgress(i++, c.getName());
331 } 327 }
@@ -410,7 +406,7 @@ public class Deobfuscator {
410 } 406 }
411 } 407 }
412 408
413 return m_jarIndex.containsObfEntry(obfEntry); 409 return this.jarIndex.containsObfEntry(obfEntry);
414 } 410 }
415 411
416 public boolean isRenameable(EntryReference<Entry, Entry> obfReference) { 412 public boolean isRenameable(EntryReference<Entry, Entry> obfReference) {
@@ -423,7 +419,7 @@ public class Deobfuscator {
423 Translator translator = getTranslator(TranslationDirection.Deobfuscating); 419 Translator translator = getTranslator(TranslationDirection.Deobfuscating);
424 if (obfEntry instanceof ClassEntry) { 420 if (obfEntry instanceof ClassEntry) {
425 ClassEntry obfClass = (ClassEntry) obfEntry; 421 ClassEntry obfClass = (ClassEntry) obfEntry;
426 List<ClassMapping> mappingChain = m_mappings.getClassMappingChain(obfClass); 422 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass);
427 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); 423 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1);
428 return classMapping != null && classMapping.getDeobfName() != null; 424 return classMapping != null && classMapping.getDeobfName() != null;
429 } else if (obfEntry instanceof FieldEntry) { 425 } else if (obfEntry instanceof FieldEntry) {
@@ -442,58 +438,58 @@ public class Deobfuscator {
442 438
443 public void rename(Entry obfEntry, String newName) { 439 public void rename(Entry obfEntry, String newName) {
444 if (obfEntry instanceof ClassEntry) { 440 if (obfEntry instanceof ClassEntry) {
445 m_renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); 441 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName));
446 } else if (obfEntry instanceof FieldEntry) { 442 } else if (obfEntry instanceof FieldEntry) {
447 m_renamer.setFieldName((FieldEntry) obfEntry, newName); 443 this.renamer.setFieldName((FieldEntry) obfEntry, newName);
448 } else if (obfEntry instanceof MethodEntry) { 444 } else if (obfEntry instanceof MethodEntry) {
449 m_renamer.setMethodTreeName((MethodEntry) obfEntry, newName); 445 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName);
450 } else if (obfEntry instanceof ConstructorEntry) { 446 } else if (obfEntry instanceof ConstructorEntry) {
451 throw new IllegalArgumentException("Cannot rename constructors"); 447 throw new IllegalArgumentException("Cannot rename constructors");
452 } else if (obfEntry instanceof ArgumentEntry) { 448 } else if (obfEntry instanceof ArgumentEntry) {
453 m_renamer.setArgumentName((ArgumentEntry) obfEntry, newName); 449 this.renamer.setArgumentName((ArgumentEntry) obfEntry, newName);
454 } else { 450 } else {
455 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 451 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
456 } 452 }
457 453
458 // clear caches 454 // clear caches
459 m_translatorCache.clear(); 455 this.translatorCache.clear();
460 } 456 }
461 457
462 public void removeMapping(Entry obfEntry) { 458 public void removeMapping(Entry obfEntry) {
463 if (obfEntry instanceof ClassEntry) { 459 if (obfEntry instanceof ClassEntry) {
464 m_renamer.removeClassMapping((ClassEntry) obfEntry); 460 this.renamer.removeClassMapping((ClassEntry) obfEntry);
465 } else if (obfEntry instanceof FieldEntry) { 461 } else if (obfEntry instanceof FieldEntry) {
466 m_renamer.removeFieldMapping((FieldEntry) obfEntry); 462 this.renamer.removeFieldMapping((FieldEntry) obfEntry);
467 } else if (obfEntry instanceof MethodEntry) { 463 } else if (obfEntry instanceof MethodEntry) {
468 m_renamer.removeMethodTreeMapping((MethodEntry) obfEntry); 464 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry);
469 } else if (obfEntry instanceof ConstructorEntry) { 465 } else if (obfEntry instanceof ConstructorEntry) {
470 throw new IllegalArgumentException("Cannot rename constructors"); 466 throw new IllegalArgumentException("Cannot rename constructors");
471 } else if (obfEntry instanceof ArgumentEntry) { 467 } else if (obfEntry instanceof ArgumentEntry) {
472 m_renamer.removeArgumentMapping((ArgumentEntry) obfEntry); 468 this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry);
473 } else { 469 } else {
474 throw new Error("Unknown entry type: " + obfEntry); 470 throw new Error("Unknown entry type: " + obfEntry);
475 } 471 }
476 472
477 // clear caches 473 // clear caches
478 m_translatorCache.clear(); 474 this.translatorCache.clear();
479 } 475 }
480 476
481 public void markAsDeobfuscated(Entry obfEntry) { 477 public void markAsDeobfuscated(Entry obfEntry) {
482 if (obfEntry instanceof ClassEntry) { 478 if (obfEntry instanceof ClassEntry) {
483 m_renamer.markClassAsDeobfuscated((ClassEntry) obfEntry); 479 this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry);
484 } else if (obfEntry instanceof FieldEntry) { 480 } else if (obfEntry instanceof FieldEntry) {
485 m_renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry); 481 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry);
486 } else if (obfEntry instanceof MethodEntry) { 482 } else if (obfEntry instanceof MethodEntry) {
487 m_renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry); 483 this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry);
488 } else if (obfEntry instanceof ConstructorEntry) { 484 } else if (obfEntry instanceof ConstructorEntry) {
489 throw new IllegalArgumentException("Cannot rename constructors"); 485 throw new IllegalArgumentException("Cannot rename constructors");
490 } else if (obfEntry instanceof ArgumentEntry) { 486 } else if (obfEntry instanceof ArgumentEntry) {
491 m_renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); 487 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry);
492 } else { 488 } else {
493 throw new Error("Unknown entry type: " + obfEntry); 489 throw new Error("Unknown entry type: " + obfEntry);
494 } 490 }
495 491
496 // clear caches 492 // clear caches
497 m_translatorCache.clear(); 493 this.translatorCache.clear();
498 } 494 }
499} 495}
diff --git a/src/main/java/cuchaz/enigma/MainFormatConverter.java b/src/main/java/cuchaz/enigma/MainFormatConverter.java
deleted file mode 100644
index 4dd19ea8..00000000
--- a/src/main/java/cuchaz/enigma/MainFormatConverter.java
+++ /dev/null
@@ -1,113 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma;
12
13import com.google.common.collect.Maps;
14
15import java.io.File;
16import java.lang.reflect.Field;
17import java.util.Map;
18import java.util.jar.JarFile;
19
20import cuchaz.enigma.analysis.JarClassIterator;
21import cuchaz.enigma.mapping.*;
22import javassist.CtClass;
23import javassist.CtField;
24
25public class MainFormatConverter {
26
27 public static void main(String[] args)
28 throws Exception {
29
30 System.out.println("Getting field types from jar...");
31
32 JarFile jar = new JarFile(System.getProperty("user.home") + "/.minecraft/versions/1.8/1.8.jar");
33 Map<String, Type> fieldTypes = Maps.newHashMap();
34 for (CtClass c : JarClassIterator.classes(jar)) {
35 for (CtField field : c.getDeclaredFields()) {
36 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
37 fieldTypes.put(getFieldKey(fieldEntry), moveClasssesOutOfDefaultPackage(fieldEntry.getType()));
38 }
39 }
40
41 System.out.println("Reading mappings...");
42
43 File fileMappings = new File("../Enigma Mappings/1.8.mappings");
44 MappingsReader mappingsReader = new MappingsReader() {
45
46 @Override
47 protected FieldMapping readField(String obf, String deobf, String type) {
48 // assume the void type for now
49 return new FieldMapping(obf, new Type("V"), deobf);
50 }
51 };
52 Mappings mappings = mappingsReader.read(fileMappings);
53
54 System.out.println("Updating field types...");
55
56 for (ClassMapping classMapping : mappings.classes()) {
57 updateFieldsInClass(fieldTypes, classMapping);
58 }
59
60 System.out.println("Saving mappings...");
61
62 new MappingsWriter().write(fileMappings, mappings);
63
64 System.out.println("Done!");
65 }
66
67 private static Type moveClasssesOutOfDefaultPackage(Type type) {
68 return new Type(type, className -> {
69 ClassEntry entry = new ClassEntry(className);
70 if (entry.isInDefaultPackage()) {
71 return Constants.NONE_PACKAGE + "/" + className;
72 }
73 return null;
74 });
75 }
76
77 private static void updateFieldsInClass(Map<String, Type> fieldTypes, ClassMapping classMapping)
78 throws Exception {
79
80 // update the fields
81 for (FieldMapping fieldMapping : classMapping.fields()) {
82 setFieldType(fieldTypes, classMapping, fieldMapping);
83 }
84
85 // recurse
86 for (ClassMapping innerClassMapping : classMapping.innerClasses()) {
87 updateFieldsInClass(fieldTypes, innerClassMapping);
88 }
89 }
90
91 private static void setFieldType(Map<String, Type> fieldTypes, ClassMapping classMapping, FieldMapping fieldMapping)
92 throws Exception {
93
94 // get the new type
95 Type newType = fieldTypes.get(getFieldKey(classMapping, fieldMapping));
96 if (newType == null) {
97 throw new Error("Can't find type for field: " + getFieldKey(classMapping, fieldMapping));
98 }
99
100 // hack in the new field type
101 Field field = fieldMapping.getClass().getDeclaredField("m_obfType");
102 field.setAccessible(true);
103 field.set(fieldMapping, newType);
104 }
105
106 private static Object getFieldKey(ClassMapping classMapping, FieldMapping fieldMapping) {
107 return classMapping.getObfSimpleName() + "." + fieldMapping.getObfName();
108 }
109
110 private static String getFieldKey(FieldEntry obfFieldEntry) {
111 return obfFieldEntry.getClassEntry().getSimpleName() + "." + obfFieldEntry.getName();
112 }
113}
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
index 942c8308..ad1d244d 100644
--- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
@@ -35,24 +35,20 @@ import javassist.bytecode.Descriptor;
35 35
36public class TranslatingTypeLoader implements ITypeLoader { 36public class TranslatingTypeLoader implements ITypeLoader {
37 37
38 private JarFile m_jar; 38 private JarFile jar;
39 private JarIndex m_jarIndex; 39 private JarIndex jarIndex;
40 private Translator m_obfuscatingTranslator; 40 private Translator obfuscatingTranslator;
41 private Translator m_deobfuscatingTranslator; 41 private Translator deobfuscatingTranslator;
42 private Map<String, byte[]> m_cache; 42 private Map<String, byte[]> cache;
43 private ClasspathTypeLoader m_defaultTypeLoader; 43 private ClasspathTypeLoader defaultTypeLoader;
44
45 public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex) {
46 this(jar, jarIndex, new Translator(), new Translator());
47 }
48 44
49 public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { 45 public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) {
50 m_jar = jar; 46 this.jar = jar;
51 m_jarIndex = jarIndex; 47 this.jarIndex = jarIndex;
52 m_obfuscatingTranslator = obfuscatingTranslator; 48 this.obfuscatingTranslator = obfuscatingTranslator;
53 m_deobfuscatingTranslator = deobfuscatingTranslator; 49 this.deobfuscatingTranslator = deobfuscatingTranslator;
54 m_cache = Maps.newHashMap(); 50 this.cache = Maps.newHashMap();
55 m_defaultTypeLoader = new ClasspathTypeLoader(); 51 this.defaultTypeLoader = new ClasspathTypeLoader();
56 } 52 }
57 53
58 @Override 54 @Override
@@ -60,16 +56,16 @@ public class TranslatingTypeLoader implements ITypeLoader {
60 56
61 // check the cache 57 // check the cache
62 byte[] data; 58 byte[] data;
63 if (m_cache.containsKey(className)) { 59 if (this.cache.containsKey(className)) {
64 data = m_cache.get(className); 60 data = this.cache.get(className);
65 } else { 61 } else {
66 data = loadType(className); 62 data = loadType(className);
67 m_cache.put(className, data); 63 this.cache.put(className, data);
68 } 64 }
69 65
70 if (data == null) { 66 if (data == null) {
71 // chain to default type loader 67 // chain to default type loader
72 return m_defaultTypeLoader.tryLoadType(className, out); 68 return this.defaultTypeLoader.tryLoadType(className, out);
73 } 69 }
74 70
75 // send the class to the decompiler 71 // send the class to the decompiler
@@ -79,33 +75,15 @@ public class TranslatingTypeLoader implements ITypeLoader {
79 return true; 75 return true;
80 } 76 }
81 77
82 public CtClass loadClass(String deobfClassName) {
83
84 byte[] data = loadType(deobfClassName);
85 if (data == null) {
86 return null;
87 }
88
89 // return a javassist handle for the class
90 String javaClassFileName = Descriptor.toJavaName(deobfClassName);
91 ClassPool classPool = new ClassPool();
92 classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data));
93 try {
94 return classPool.get(javaClassFileName);
95 } catch (NotFoundException ex) {
96 throw new Error(ex);
97 }
98 }
99
100 private byte[] loadType(String className) { 78 private byte[] loadType(String className) {
101 79
102 // NOTE: don't know if class name is obf or deobf 80 // NOTE: don't know if class name is obf or deobf
103 ClassEntry classEntry = new ClassEntry(className); 81 ClassEntry classEntry = new ClassEntry(className);
104 ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(classEntry); 82 ClassEntry obfClassEntry = this.obfuscatingTranslator.translateEntry(classEntry);
105 83
106 // is this an inner class referenced directly? (ie trying to load b instead of a$b) 84 // is this an inner class referenced directly? (ie trying to load b instead of a$b)
107 if (!obfClassEntry.isInnerClass()) { 85 if (!obfClassEntry.isInnerClass()) {
108 List<ClassEntry> classChain = m_jarIndex.getObfClassChain(obfClassEntry); 86 List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry);
109 if (classChain.size() > 1) { 87 if (classChain.size() > 1) {
110 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s", 88 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s",
111 className, obfClassEntry.buildClassEntry(classChain) 89 className, obfClassEntry.buildClassEntry(classChain)
@@ -115,7 +93,7 @@ public class TranslatingTypeLoader implements ITypeLoader {
115 } 93 }
116 94
117 // is this a class we should even know about? 95 // is this a class we should even know about?
118 if (!m_jarIndex.containsObfClass(obfClassEntry)) { 96 if (!this.jarIndex.containsObfClass(obfClassEntry)) {
119 return null; 97 return null;
120 } 98 }
121 99
@@ -133,7 +111,7 @@ public class TranslatingTypeLoader implements ITypeLoader {
133 // read the class file into a buffer 111 // read the class file into a buffer
134 ByteArrayOutputStream data = new ByteArrayOutputStream(); 112 ByteArrayOutputStream data = new ByteArrayOutputStream();
135 byte[] buf = new byte[1024 * 1024]; // 1 KiB 113 byte[] buf = new byte[1024 * 1024]; // 1 KiB
136 InputStream in = m_jar.getInputStream(m_jar.getJarEntry(classInJarName + ".class")); 114 InputStream in = this.jar.getInputStream(this.jar.getJarEntry(classInJarName + ".class"));
137 while (true) { 115 while (true) {
138 int bytesRead = in.read(buf); 116 int bytesRead = in.read(buf);
139 if (bytesRead <= 0) { 117 if (bytesRead <= 0) {
@@ -170,7 +148,7 @@ public class TranslatingTypeLoader implements ITypeLoader {
170 148
171 // try to find the class in the jar 149 // try to find the class in the jar
172 for (String className : getClassNamesToTry(obfClassEntry)) { 150 for (String className : getClassNamesToTry(obfClassEntry)) {
173 JarEntry jarEntry = m_jar.getJarEntry(className + ".class"); 151 JarEntry jarEntry = this.jar.getJarEntry(className + ".class");
174 if (jarEntry != null) { 152 if (jarEntry != null) {
175 return className; 153 return className;
176 } 154 }
@@ -181,7 +159,7 @@ public class TranslatingTypeLoader implements ITypeLoader {
181 } 159 }
182 160
183 public List<String> getClassNamesToTry(String className) { 161 public List<String> getClassNamesToTry(String className) {
184 return getClassNamesToTry(m_obfuscatingTranslator.translateEntry(new ClassEntry(className))); 162 return getClassNamesToTry(this.obfuscatingTranslator.translateEntry(new ClassEntry(className)));
185 } 163 }
186 164
187 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { 165 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) {
@@ -206,7 +184,7 @@ public class TranslatingTypeLoader implements ITypeLoader {
206 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NONE_PACKAGE); 184 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NONE_PACKAGE);
207 185
208 // reconstruct inner classes 186 // reconstruct inner classes
209 new InnerClassWriter(m_jarIndex).write(c); 187 new InnerClassWriter(this.jarIndex).write(c);
210 188
211 // re-get the javassist handle since we changed class names 189 // re-get the javassist handle since we changed class names
212 ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 190 ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
@@ -219,10 +197,10 @@ public class TranslatingTypeLoader implements ITypeLoader {
219 assertClassName(c, obfClassEntry); 197 assertClassName(c, obfClassEntry);
220 198
221 // do all kinds of deobfuscating transformations on the class 199 // do all kinds of deobfuscating transformations on the class
222 new BridgeMarker(m_jarIndex).markBridges(c); 200 new BridgeMarker(this.jarIndex).markBridges(c);
223 new MethodParameterWriter(m_deobfuscatingTranslator).writeMethodArguments(c); 201 new MethodParameterWriter(this.deobfuscatingTranslator).writeMethodArguments(c);
224 new LocalVariableRenamer(m_deobfuscatingTranslator).rename(c); 202 new LocalVariableRenamer(this.deobfuscatingTranslator).rename(c);
225 new ClassTranslator(m_deobfuscatingTranslator).translate(c); 203 new ClassTranslator(this.deobfuscatingTranslator).translate(c);
226 204
227 return c; 205 return c;
228 } 206 }
diff --git a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
index 39ff449e..98aa12e5 100644
--- a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
@@ -23,8 +23,6 @@ import cuchaz.enigma.mapping.Translator;
23 23
24public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> { 24public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> {
25 25
26 private static final long serialVersionUID = -3658163700783307520L;
27
28 private Translator m_deobfuscatingTranslator; 26 private Translator m_deobfuscatingTranslator;
29 private BehaviorEntry m_entry; 27 private BehaviorEntry m_entry;
30 private EntryReference<BehaviorEntry, BehaviorEntry> m_reference; 28 private EntryReference<BehaviorEntry, BehaviorEntry> m_reference;
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
index b1d1dee5..f5227bb9 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -21,8 +21,6 @@ import cuchaz.enigma.mapping.Translator;
21 21
22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { 22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
23 23
24 private static final long serialVersionUID = 3112703459157851912L;
25
26 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
27 private ClassEntry entry; 25 private ClassEntry entry;
28 26
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index beac9d5d..8a60fc72 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -21,8 +21,6 @@ import cuchaz.enigma.mapping.Translator;
21 21
22public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { 22public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
23 23
24 private static final long serialVersionUID = 4432367405826178490L;
25
26 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
27 private String obfClassName; 25 private String obfClassName;
28 26
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
index 6a7a4bf0..ad4baf81 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -13,10 +13,10 @@ package cuchaz.enigma.analysis;
13import java.util.Arrays; 13import java.util.Arrays;
14import java.util.List; 14import java.util.List;
15 15
16import cuchaz.enigma.Util;
17import cuchaz.enigma.mapping.ClassEntry; 16import cuchaz.enigma.mapping.ClassEntry;
18import cuchaz.enigma.mapping.ConstructorEntry; 17import cuchaz.enigma.mapping.ConstructorEntry;
19import cuchaz.enigma.mapping.Entry; 18import cuchaz.enigma.mapping.Entry;
19import cuchaz.enigma.utils.Utils;
20 20
21public class EntryReference<E extends Entry, C extends Entry> { 21public class EntryReference<E extends Entry, C extends Entry> {
22 22
@@ -84,7 +84,7 @@ public class EntryReference<E extends Entry, C extends Entry> {
84 @Override 84 @Override
85 public int hashCode() { 85 public int hashCode() {
86 if (context != null) { 86 if (context != null) {
87 return Util.combineHashesOrdered(entry.hashCode(), context.hashCode()); 87 return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode());
88 } 88 }
89 return entry.hashCode(); 89 return entry.hashCode();
90 } 90 }
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
index 9a85f6ee..f0e73062 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
@@ -57,35 +57,6 @@ public class EntryRenamer {
57 } 57 }
58 58
59 @SuppressWarnings("unchecked") 59 @SuppressWarnings("unchecked")
60 public static <T> T renameMethodsInThing(Map<MethodEntry, MethodEntry> renames, T thing) {
61 if (thing instanceof MethodEntry) {
62 MethodEntry methodEntry = (MethodEntry) thing;
63 MethodEntry newMethodEntry = renames.get(methodEntry);
64 if (newMethodEntry != null) {
65 return (T) new MethodEntry(
66 methodEntry.getClassEntry(),
67 newMethodEntry.getName(),
68 methodEntry.getSignature()
69 );
70 }
71 return thing;
72 } else if (thing instanceof ArgumentEntry) {
73 ArgumentEntry argumentEntry = (ArgumentEntry) thing;
74 return (T) new ArgumentEntry(
75 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()),
76 argumentEntry.getIndex(),
77 argumentEntry.getName()
78 );
79 } else if (thing instanceof EntryReference) {
80 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
81 reference.entry = renameMethodsInThing(renames, reference.entry);
82 reference.context = renameMethodsInThing(renames, reference.context);
83 return thing;
84 }
85 return thing;
86 }
87
88 @SuppressWarnings("unchecked")
89 public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) { 60 public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) {
90 if (thing instanceof String) { 61 if (thing instanceof String) {
91 String stringEntry = (String) thing; 62 String stringEntry = (String) thing;
@@ -97,31 +68,16 @@ public class EntryRenamer {
97 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); 68 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName()));
98 } else if (thing instanceof FieldEntry) { 69 } else if (thing instanceof FieldEntry) {
99 FieldEntry fieldEntry = (FieldEntry) thing; 70 FieldEntry fieldEntry = (FieldEntry) thing;
100 return (T) new FieldEntry( 71 return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType()));
101 renameClassesInThing(renames, fieldEntry.getClassEntry()),
102 fieldEntry.getName(),
103 renameClassesInThing(renames, fieldEntry.getType())
104 );
105 } else if (thing instanceof ConstructorEntry) { 72 } else if (thing instanceof ConstructorEntry) {
106 ConstructorEntry constructorEntry = (ConstructorEntry) thing; 73 ConstructorEntry constructorEntry = (ConstructorEntry) thing;
107 return (T) new ConstructorEntry( 74 return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature()));
108 renameClassesInThing(renames, constructorEntry.getClassEntry()),
109 renameClassesInThing(renames, constructorEntry.getSignature())
110 );
111 } else if (thing instanceof MethodEntry) { 75 } else if (thing instanceof MethodEntry) {
112 MethodEntry methodEntry = (MethodEntry) thing; 76 MethodEntry methodEntry = (MethodEntry) thing;
113 return (T) new MethodEntry( 77 return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature()));
114 renameClassesInThing(renames, methodEntry.getClassEntry()),
115 methodEntry.getName(),
116 renameClassesInThing(renames, methodEntry.getSignature())
117 );
118 } else if (thing instanceof ArgumentEntry) { 78 } else if (thing instanceof ArgumentEntry) {
119 ArgumentEntry argumentEntry = (ArgumentEntry) thing; 79 ArgumentEntry argumentEntry = (ArgumentEntry) thing;
120 return (T) new ArgumentEntry( 80 return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName());
121 renameClassesInThing(renames, argumentEntry.getBehaviorEntry()),
122 argumentEntry.getIndex(),
123 argumentEntry.getName()
124 );
125 } else if (thing instanceof EntryReference) { 81 } else if (thing instanceof EntryReference) {
126 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 82 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
127 reference.entry = renameClassesInThing(renames, reference.entry); 83 reference.entry = renameClassesInThing(renames, reference.entry);
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 05d9c13f..70cd0590 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -18,8 +18,6 @@ import cuchaz.enigma.mapping.Translator;
18 18
19public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> { 19public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> {
20 20
21 private static final long serialVersionUID = -7934108091928699835L;
22
23 private Translator deobfuscatingTranslator; 21 private Translator deobfuscatingTranslator;
24 private FieldEntry entry; 22 private FieldEntry entry;
25 private EntryReference<FieldEntry, BehaviorEntry> reference; 23 private EntryReference<FieldEntry, BehaviorEntry> reference;
diff --git a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
index 04004270..0d18105e 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
@@ -89,14 +89,6 @@ public class JarClassIterator implements Iterator<CtClass> {
89 return () -> new JarClassIterator(jar); 89 return () -> new JarClassIterator(jar);
90 } 90 }
91 91
92 public static CtClass getClass(JarFile jar, ClassEntry classEntry) {
93 try {
94 return getClass(jar, new JarEntry(classEntry.getName() + ".class"));
95 } catch (IOException | NotFoundException ex) {
96 throw new Error("Unable to load class: " + classEntry.getName());
97 }
98 }
99
100 private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { 92 private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException {
101 // read the class into a buffer 93 // read the class into a buffer
102 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 94 ByteArrayOutputStream bos = new ByteArrayOutputStream();
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
index bd670905..9acf0b14 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
@@ -503,14 +503,6 @@ public class JarIndex {
503 return this.obfClassEntries; 503 return this.obfClassEntries;
504 } 504 }
505 505
506 public Collection<FieldEntry> getObfFieldEntries() {
507 return this.fields.values();
508 }
509
510 public Collection<BehaviorEntry> getObfBehaviorEntries() {
511 return this.behaviors.values();
512 }
513
514 public TranslationIndex getTranslationIndex() { 506 public TranslationIndex getTranslationIndex() {
515 return this.translationIndex; 507 return this.translationIndex;
516 } 508 }
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index 7919b5d5..9bd6219a 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -22,8 +22,6 @@ import cuchaz.enigma.mapping.Translator;
22 22
23public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { 23public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
24 24
25 private static final long serialVersionUID = 3781080657461899915L;
26
27 private Translator deobfuscatingTranslator; 25 private Translator deobfuscatingTranslator;
28 private MethodEntry entry; 26 private MethodEntry entry;
29 27
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index 63837830..b65b8c10 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -22,8 +22,6 @@ import cuchaz.enigma.mapping.Translator;
22 22
23public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { 23public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
24 24
25 private static final long serialVersionUID = 1096677030991810007L;
26
27 private Translator deobfuscatingTranslator; 25 private Translator deobfuscatingTranslator;
28 private MethodEntry entry; 26 private MethodEntry entry;
29 private boolean isImplemented; 27 private boolean isImplemented;
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
index 3fa1309a..73e04319 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -145,10 +145,6 @@ public class SourceIndex {
145 return this.tokenToReference.keySet(); 145 return this.tokenToReference.keySet();
146 } 146 }
147 147
148 public Iterable<Entry> declarations() {
149 return this.declarationToToken.keySet();
150 }
151
152 public Token getDeclarationToken(Entry deobfEntry) { 148 public Token getDeclarationToken(Entry deobfEntry) {
153 return this.declarationToToken.get(deobfEntry); 149 return this.declarationToToken.get(deobfEntry);
154 } 150 }
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java
index 575c373f..419842af 100644
--- a/src/main/java/cuchaz/enigma/analysis/Token.java
+++ b/src/main/java/cuchaz/enigma/analysis/Token.java
@@ -16,10 +16,6 @@ public class Token implements Comparable<Token> {
16 public int end; 16 public int end;
17 public String text; 17 public String text;
18 18
19 public Token(int start, int end) {
20 this(start, end, null);
21 }
22
23 public Token(int start, int end, String source) { 19 public Token(int start, int end, String source) {
24 this.start = start; 20 this.start = start;
25 this.end = end; 21 this.end = end;
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
index 3d8f5560..275f56c9 100644
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
@@ -15,10 +15,10 @@ import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import com.google.common.collect.Multimap; 16import com.google.common.collect.Multimap;
17 17
18import java.io.*; 18import java.util.Collection;
19import java.util.*; 19import java.util.List;
20import java.util.zip.GZIPInputStream; 20import java.util.Map;
21import java.util.zip.GZIPOutputStream; 21import java.util.Set;
22 22
23import cuchaz.enigma.mapping.*; 23import cuchaz.enigma.mapping.*;
24import javassist.CtBehavior; 24import javassist.CtBehavior;
@@ -26,9 +26,7 @@ import javassist.CtClass;
26import javassist.CtField; 26import javassist.CtField;
27import javassist.bytecode.Descriptor; 27import javassist.bytecode.Descriptor;
28 28
29public class TranslationIndex implements Serializable { 29public class TranslationIndex {
30
31 private static final long serialVersionUID = 738687982126844179L;
32 30
33 private Map<ClassEntry, ClassEntry> superclasses; 31 private Map<ClassEntry, ClassEntry> superclasses;
34 private Multimap<ClassEntry, FieldEntry> fieldEntries; 32 private Multimap<ClassEntry, FieldEntry> fieldEntries;
@@ -43,7 +41,6 @@ public class TranslationIndex implements Serializable {
43 } 41 }
44 42
45 public TranslationIndex(TranslationIndex other, Translator translator) { 43 public TranslationIndex(TranslationIndex other, Translator translator) {
46
47 // translate the superclasses 44 // translate the superclasses
48 this.superclasses = Maps.newHashMap(); 45 this.superclasses = Maps.newHashMap();
49 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { 46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
@@ -82,7 +79,6 @@ public class TranslationIndex implements Serializable {
82 } 79 }
83 80
84 public void indexClass(CtClass c, boolean indexMembers) { 81 public void indexClass(CtClass c, boolean indexMembers) {
85
86 ClassEntry classEntry = EntryFactory.getClassEntry(c); 82 ClassEntry classEntry = EntryFactory.getClassEntry(c);
87 if (isJre(classEntry)) { 83 if (isJre(classEntry)) {
88 return; 84 return;
@@ -139,7 +135,6 @@ public class TranslationIndex implements Serializable {
139 } 135 }
140 136
141 public List<ClassEntry> getSubclass(ClassEntry classEntry) { 137 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
142
143 // linear search is fast enough for now 138 // linear search is fast enough for now
144 List<ClassEntry> subclasses = Lists.newArrayList(); 139 List<ClassEntry> subclasses = Lists.newArrayList();
145 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) { 140 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) {
@@ -191,7 +186,6 @@ public class TranslationIndex implements Serializable {
191 } 186 }
192 187
193 public ClassEntry resolveEntryClass(Entry entry) { 188 public ClassEntry resolveEntryClass(Entry entry) {
194
195 if (entry instanceof ClassEntry) { 189 if (entry instanceof ClassEntry) {
196 return (ClassEntry) entry; 190 return (ClassEntry) entry;
197 } 191 }
@@ -210,7 +204,6 @@ public class TranslationIndex implements Serializable {
210 } 204 }
211 205
212 public ClassEntry resolveSuperclass(Entry entry) { 206 public ClassEntry resolveSuperclass(Entry entry) {
213
214 // this entry could refer to a method on a class where the method is not actually implemented 207 // this entry could refer to a method on a class where the method is not actually implemented
215 // travel up the inheritance tree to find the closest implementation 208 // travel up the inheritance tree to find the closest implementation
216 while (!entryExists(entry)) { 209 while (!entryExists(entry)) {
@@ -230,7 +223,6 @@ public class TranslationIndex implements Serializable {
230 } 223 }
231 224
232 public ClassEntry resolveInterface(Entry entry) { 225 public ClassEntry resolveInterface(Entry entry) {
233
234 // the interfaces for any class is a forest 226 // the interfaces for any class is a forest
235 // so let's look at all the trees 227 // so let's look at all the trees
236 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { 228 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) {
@@ -246,27 +238,4 @@ public class TranslationIndex implements Serializable {
246 String packageName = classEntry.getPackageName(); 238 String packageName = classEntry.getPackageName();
247 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); 239 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
248 } 240 }
249
250 public void write(OutputStream out)
251 throws IOException {
252 GZIPOutputStream gzipout = new GZIPOutputStream(out);
253 ObjectOutputStream oout = new ObjectOutputStream(gzipout);
254 oout.writeObject(this.superclasses);
255 oout.writeObject(this.fieldEntries);
256 oout.writeObject(this.behaviorEntries);
257 gzipout.finish();
258 }
259
260 @SuppressWarnings("unchecked")
261 public void read(InputStream in)
262 throws IOException {
263 try {
264 ObjectInputStream oin = new ObjectInputStream(new GZIPInputStream(in));
265 this.superclasses = (HashMap<ClassEntry, ClassEntry>) oin.readObject();
266 this.fieldEntries = (HashMultimap<ClassEntry, FieldEntry>) oin.readObject();
267 this.behaviorEntries = (HashMultimap<ClassEntry, BehaviorEntry>) oin.readObject();
268 } catch (ClassNotFoundException ex) {
269 throw new Error(ex);
270 }
271 }
272} 241}
diff --git a/src/main/java/cuchaz/enigma/bytecode/CheckCastIterator.java b/src/main/java/cuchaz/enigma/bytecode/CheckCastIterator.java
deleted file mode 100644
index c47a7703..00000000
--- a/src/main/java/cuchaz/enigma/bytecode/CheckCastIterator.java
+++ /dev/null
@@ -1,113 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12
13import java.util.Iterator;
14
15import cuchaz.enigma.bytecode.CheckCastIterator.CheckCast;
16import cuchaz.enigma.mapping.ClassEntry;
17import cuchaz.enigma.mapping.MethodEntry;
18import cuchaz.enigma.mapping.Signature;
19import javassist.bytecode.*;
20
21public class CheckCastIterator implements Iterator<CheckCast> {
22
23 public static class CheckCast {
24
25 public final String className;
26 public final MethodEntry prevMethodEntry;
27
28 public CheckCast(String className, MethodEntry prevMethodEntry) {
29 this.className = className;
30 this.prevMethodEntry = prevMethodEntry;
31 }
32 }
33
34 private final ConstPool constants;
35 private final CodeAttribute attribute;
36 private final CodeIterator iter;
37 private CheckCast next;
38
39 public CheckCastIterator(CodeAttribute codeAttribute) throws BadBytecode {
40 this.constants = codeAttribute.getConstPool();
41 this.attribute = codeAttribute;
42 this.iter = this.attribute.iterator();
43
44 this.next = getNext();
45 }
46
47 @Override
48 public boolean hasNext() {
49 return this.next != null;
50 }
51
52 @Override
53 public CheckCast next() {
54 CheckCast out = this.next;
55 try {
56 this.next = getNext();
57 } catch (BadBytecode ex) {
58 throw new Error(ex);
59 }
60 return out;
61 }
62
63 @Override
64 public void remove() {
65 throw new UnsupportedOperationException();
66 }
67
68 private CheckCast getNext() throws BadBytecode {
69 int prevPos = 0;
70 while (this.iter.hasNext()) {
71 int pos = this.iter.next();
72 int opcode = this.iter.byteAt(pos);
73 switch (opcode) {
74 case Opcode.CHECKCAST:
75
76 // get the type of this op code (next two bytes are a classinfo index)
77 MethodEntry prevMethodEntry = getMethodEntry(prevPos);
78 if (prevMethodEntry != null) {
79 return new CheckCast(this.constants.getClassInfo(this.iter.s16bitAt(pos + 1)), prevMethodEntry);
80 }
81 break;
82 }
83 prevPos = pos;
84 }
85 return null;
86 }
87
88 private MethodEntry getMethodEntry(int pos) {
89 switch (this.iter.byteAt(pos)) {
90 case Opcode.INVOKEVIRTUAL:
91 case Opcode.INVOKESTATIC:
92 case Opcode.INVOKEDYNAMIC:
93 case Opcode.INVOKESPECIAL: {
94 int index = this.iter.s16bitAt(pos + 1);
95 return new MethodEntry(
96 new ClassEntry(Descriptor.toJvmName(this.constants.getMethodrefClassName(index))),
97 this.constants.getMethodrefName(index),
98 new Signature(this.constants.getMethodrefType(index))
99 );
100 }
101
102 case Opcode.INVOKEINTERFACE: {
103 int index = this.iter.s16bitAt(pos + 1);
104 return new MethodEntry(
105 new ClassEntry(Descriptor.toJvmName(this.constants.getInterfaceMethodrefClassName(index))),
106 this.constants.getInterfaceMethodrefName(index),
107 new Signature(this.constants.getInterfaceMethodrefType(index))
108 );
109 }
110 }
111 return null;
112 }
113}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
index e49ffdbd..af8c79a1 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
@@ -17,7 +17,6 @@ import java.lang.reflect.Field;
17import java.lang.reflect.Method; 17import java.lang.reflect.Method;
18import java.util.HashMap; 18import java.util.HashMap;
19 19
20import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor;
21import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; 20import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
22import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; 21import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;
23import javassist.bytecode.ConstPool; 22import javassist.bytecode.ConstPool;
@@ -101,32 +100,7 @@ public class ConstPoolEditor {
101 throw new Error(ex); 100 throw new Error(ex);
102 } 101 }
103 } 102 }
104 @SuppressWarnings("rawtypes")
105 public void removeLastItem() {
106 try {
107 // remove the item from the cache
108 HashMap cache = getCache();
109 if (cache != null) {
110 Object item = getItem(this.pool.getSize() - 1);
111 cache.remove(item);
112 }
113 103
114 // remove the actual item
115 // based off of LongVector.addElement()
116 Object item = items.get(this.pool);
117 Object[][] object = (Object[][]) objects.get(items);
118 int numElements = (Integer) elements.get(items) - 1;
119 int nth = numElements >> 7;
120 int offset = numElements & (128 - 1);
121 object[nth][offset] = null;
122
123 // decrement the number of items
124 elements.set(item, numElements);
125 numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1);
126 } catch (Exception ex) {
127 throw new Error(ex);
128 }
129 }
130 104
131 @SuppressWarnings("rawtypes") 105 @SuppressWarnings("rawtypes")
132 public HashMap getCache() { 106 public HashMap getCache() {
diff --git a/src/main/java/cuchaz/enigma/bytecode/InfoType.java b/src/main/java/cuchaz/enigma/bytecode/InfoType.java
index bd9e7d1a..21b04173 100644
--- a/src/main/java/cuchaz/enigma/bytecode/InfoType.java
+++ b/src/main/java/cuchaz/enigma/bytecode/InfoType.java
@@ -10,23 +10,21 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode; 11package cuchaz.enigma.bytecode;
12 12
13import com.google.common.collect.Lists;
14import com.google.common.collect.Maps; 13import com.google.common.collect.Maps;
15 14
16import java.util.Collection; 15import java.util.Collection;
17import java.util.List;
18import java.util.Map; 16import java.util.Map;
19 17
20import cuchaz.enigma.bytecode.accessors.*; 18import cuchaz.enigma.bytecode.accessors.*;
21 19
22public enum InfoType { 20public enum InfoType {
23 21
24 Utf8Info(1, 0), 22 Utf8Info(1),
25 IntegerInfo(3, 0), 23 IntegerInfo(3),
26 FloatInfo(4, 0), 24 FloatInfo(4),
27 LongInfo(5, 0), 25 LongInfo(5),
28 DoubleInfo(6, 0), 26 DoubleInfo(6),
29 ClassInfo(7, 1) { 27 ClassInfo(7) {
30 @Override 28 @Override
31 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 29 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
32 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); 30 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
@@ -46,7 +44,7 @@ public enum InfoType {
46 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); 44 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag();
47 } 45 }
48 }, 46 },
49 StringInfo(8, 1) { 47 StringInfo(8) {
50 @Override 48 @Override
51 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 49 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
52 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); 50 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
@@ -66,7 +64,7 @@ public enum InfoType {
66 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); 64 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag();
67 } 65 }
68 }, 66 },
69 FieldRefInfo(9, 2) { 67 FieldRefInfo(9) {
70 @Override 68 @Override
71 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 69 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
72 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); 70 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
@@ -90,7 +88,7 @@ public enum InfoType {
90 } 88 }
91 }, 89 },
92 // same as FieldRefInfo 90 // same as FieldRefInfo
93 MethodRefInfo(10, 2) { 91 MethodRefInfo(10) {
94 @Override 92 @Override
95 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 93 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
96 FieldRefInfo.gatherIndexTree(indices, editor, entry); 94 FieldRefInfo.gatherIndexTree(indices, editor, entry);
@@ -107,7 +105,7 @@ public enum InfoType {
107 } 105 }
108 }, 106 },
109 // same as FieldRefInfo 107 // same as FieldRefInfo
110 InterfaceMethodRefInfo(11, 2) { 108 InterfaceMethodRefInfo(11) {
111 @Override 109 @Override
112 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 110 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
113 FieldRefInfo.gatherIndexTree(indices, editor, entry); 111 FieldRefInfo.gatherIndexTree(indices, editor, entry);
@@ -123,7 +121,7 @@ public enum InfoType {
123 return FieldRefInfo.subIndicesAreValid(entry, pool); 121 return FieldRefInfo.subIndicesAreValid(entry, pool);
124 } 122 }
125 }, 123 },
126 NameAndTypeInfo(12, 1) { 124 NameAndTypeInfo(12) {
127 @Override 125 @Override
128 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 126 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
129 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); 127 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
@@ -146,7 +144,7 @@ public enum InfoType {
146 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); 144 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
147 } 145 }
148 }, 146 },
149 MethodHandleInfo(15, 3) { 147 MethodHandleInfo(15) {
150 @Override 148 @Override
151 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 149 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
152 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); 150 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
@@ -169,7 +167,7 @@ public enum InfoType {
169 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); 167 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag();
170 } 168 }
171 }, 169 },
172 MethodTypeInfo(16, 1) { 170 MethodTypeInfo(16) {
173 @Override 171 @Override
174 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 172 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
175 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); 173 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
@@ -189,7 +187,7 @@ public enum InfoType {
189 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); 187 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
190 } 188 }
191 }, 189 },
192 InvokeDynamicInfo(18, 2) { 190 InvokeDynamicInfo(18) {
193 @Override 191 @Override
194 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 192 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
195 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); 193 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
@@ -223,21 +221,15 @@ public enum InfoType {
223 } 221 }
224 222
225 private int tag; 223 private int tag;
226 private int level;
227 224
228 InfoType(int tag, int level) { 225 InfoType(int tag) {
229 this.tag = tag; 226 this.tag = tag;
230 this.level = level;
231 } 227 }
232 228
233 public int getTag() { 229 public int getTag() {
234 return this.tag; 230 return this.tag;
235 } 231 }
236 232
237 public int getLevel() {
238 return this.level;
239 }
240
241 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 233 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
242 // by default, do nothing 234 // by default, do nothing
243 } 235 }
@@ -251,34 +243,10 @@ public enum InfoType {
251 return true; 243 return true;
252 } 244 }
253 245
254 public boolean selfIndexIsValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
255 ConstInfoAccessor entryCheck = pool.getItem(entry.getIndex());
256 return entryCheck != null && entryCheck.getItem().equals(entry.getItem());
257 }
258
259 public static InfoType getByTag(int tag) { 246 public static InfoType getByTag(int tag) {
260 return types.get(tag); 247 return types.get(tag);
261 } 248 }
262 249
263 public static List<InfoType> getByLevel(int level) {
264 List<InfoType> types = Lists.newArrayList();
265 for (InfoType type : values()) {
266 if (type.getLevel() == level) {
267 types.add(type);
268 }
269 }
270 return types;
271 }
272
273 public static List<InfoType> getSortedByLevel() {
274 List<InfoType> types = Lists.newArrayList();
275 types.addAll(getByLevel(0));
276 types.addAll(getByLevel(1));
277 types.addAll(getByLevel(2));
278 types.addAll(getByLevel(3));
279 return types;
280 }
281
282 public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) { 250 public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) {
283 // add own index 251 // add own index
284 indices.add(index); 252 indices.add(index);
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
index 2ecda1d9..0ea2d02b 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
@@ -10,8 +10,8 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode.accessors; 11package cuchaz.enigma.bytecode.accessors;
12 12
13import java.io.*; 13import java.io.ByteArrayOutputStream;
14import java.lang.reflect.Constructor; 14import java.io.PrintWriter;
15import java.lang.reflect.Field; 15import java.lang.reflect.Field;
16import java.lang.reflect.Method; 16import java.lang.reflect.Method;
17 17
@@ -32,26 +32,6 @@ public class ConstInfoAccessor {
32 this.item = item; 32 this.item = item;
33 } 33 }
34 34
35 public ConstInfoAccessor(DataInputStream in) throws IOException {
36 try {
37 // read the entry
38 String className = in.readUTF();
39 int oldIndex = in.readInt();
40
41 // NOTE: ConstInfo instances write a type id (a "tag"), but they don't read it back
42 // so we have to read it here
43 in.readByte();
44
45 Constructor<?> constructor = Class.forName(className).getConstructor(DataInputStream.class, int.class);
46 constructor.setAccessible(true);
47 this.item = constructor.newInstance(in, oldIndex);
48 } catch (IOException ex) {
49 throw ex;
50 } catch (Exception ex) {
51 throw new Error(ex);
52 }
53 }
54
55 public Object getItem() { 35 public Object getItem() {
56 return this.item; 36 return this.item;
57 } 37 }
@@ -64,14 +44,6 @@ public class ConstInfoAccessor {
64 } 44 }
65 } 45 }
66 46
67 public void setIndex(int val) {
68 try {
69 index.set(this.item, val);
70 } catch (Exception ex) {
71 throw new Error(ex);
72 }
73 }
74
75 public int getTag() { 47 public int getTag() {
76 try { 48 try {
77 return (Integer) getTag.invoke(this.item); 49 return (Integer) getTag.invoke(this.item);
@@ -80,21 +52,6 @@ public class ConstInfoAccessor {
80 } 52 }
81 } 53 }
82 54
83 public void write(DataOutputStream out) throws IOException {
84 try {
85 out.writeUTF(this.item.getClass().getName());
86 out.writeInt(getIndex());
87
88 Method method = this.item.getClass().getMethod("write", DataOutputStream.class);
89 method.setAccessible(true);
90 method.invoke(this.item, out);
91 } catch (IOException ex) {
92 throw ex;
93 } catch (Exception ex) {
94 throw new Error(ex);
95 }
96 }
97
98 @Override 55 @Override
99 public String toString() { 56 public String toString() {
100 try { 57 try {
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 c9b44b33..00000000
--- a/src/main/java/cuchaz/enigma/convert/ClassForest.java
+++ /dev/null
@@ -1,54 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Multimap;
15
16import java.util.Collection;
17
18import cuchaz.enigma.mapping.ClassEntry;
19
20
21public class ClassForest {
22
23 private ClassIdentifier identifier;
24 private Multimap<ClassIdentity, ClassEntry> forest;
25
26 public ClassForest(ClassIdentifier identifier) {
27 this.identifier = identifier;
28 this.forest = HashMultimap.create();
29 }
30
31 public void add(ClassEntry entry) {
32 try {
33 this.forest.put(this.identifier.identify(entry), entry);
34 } catch (ClassNotFoundException ex) {
35 throw new Error("Unable to find class " + entry.getName());
36 }
37 }
38
39 public Collection<ClassIdentity> identities() {
40 return this.forest.keySet();
41 }
42
43 public Collection<ClassEntry> classes() {
44 return this.forest.values();
45 }
46
47 public Collection<ClassEntry> getClasses(ClassIdentity identity) {
48 return this.forest.get(identity);
49 }
50
51 public boolean containsIdentity(ClassIdentity identity) {
52 return this.forest.containsKey(identity);
53 }
54}
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 cc7f25b8..00000000
--- a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java
+++ /dev/null
@@ -1,54 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.Maps;
14
15import java.util.Map;
16import java.util.jar.JarFile;
17
18import cuchaz.enigma.TranslatingTypeLoader;
19import cuchaz.enigma.analysis.JarIndex;
20import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
21import cuchaz.enigma.mapping.ClassEntry;
22import javassist.CtClass;
23
24
25public class ClassIdentifier {
26
27 private JarIndex index;
28 private SidedClassNamer namer;
29 private boolean useReferences;
30 private TranslatingTypeLoader loader;
31 private Map<ClassEntry, ClassIdentity> cache;
32
33 public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) {
34 this.index = index;
35 this.namer = namer;
36 this.useReferences = useReferences;
37 this.loader = new TranslatingTypeLoader(jar, index);
38 this.cache = Maps.newHashMap();
39 }
40
41 public ClassIdentity identify(ClassEntry classEntry)
42 throws ClassNotFoundException {
43 ClassIdentity identity = this.cache.get(classEntry);
44 if (identity == null) {
45 CtClass c = this.loader.loadClass(classEntry.getName());
46 if (c == null) {
47 throw new ClassNotFoundException(classEntry.getName());
48 }
49 identity = new ClassIdentity(c, this.namer, this.index, this.useReferences);
50 this.cache.put(classEntry, identity);
51 }
52 return identity;
53 }
54}
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 606c1df1..00000000
--- a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
+++ /dev/null
@@ -1,441 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.*;
14
15import java.io.UnsupportedEncodingException;
16import java.security.MessageDigest;
17import java.security.NoSuchAlgorithmException;
18import java.util.Enumeration;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22
23import cuchaz.enigma.Constants;
24import cuchaz.enigma.Util;
25import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
26import cuchaz.enigma.analysis.EntryReference;
27import cuchaz.enigma.analysis.JarIndex;
28import cuchaz.enigma.bytecode.ConstPoolEditor;
29import cuchaz.enigma.bytecode.InfoType;
30import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
31import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
32import cuchaz.enigma.mapping.*;
33import javassist.*;
34import javassist.bytecode.*;
35import javassist.expr.*;
36
37public class ClassIdentity {
38
39 private ClassEntry classEntry;
40 private SidedClassNamer namer;
41 private Multiset<String> fields;
42 private Multiset<String> methods;
43 private Multiset<String> constructors;
44 private String staticInitializer;
45 private String extendz;
46 private Multiset<String> implementz;
47 private Set<String> stringLiterals;
48 private Multiset<String> implementations;
49 private Multiset<String> references;
50 private String outer;
51
52 private final ClassNameReplacer m_classNameReplacer = new ClassNameReplacer() {
53
54 private Map<String, String> m_classNames = Maps.newHashMap();
55
56 @Override
57 public String replace(String className) {
58
59 // classes not in the none package can be passed through
60 ClassEntry classEntry = new ClassEntry(className);
61 if (!classEntry.getPackageName().equals(Constants.NONE_PACKAGE)) {
62 return className;
63 }
64
65 // is this class ourself?
66 if (className.equals(classEntry.getName())) {
67 return "CSelf";
68 }
69
70 // try the namer
71 if (namer != null) {
72 String newName = namer.getName(className);
73 if (newName != null) {
74 return newName;
75 }
76 }
77
78 // otherwise, use local naming
79 if (!m_classNames.containsKey(className)) {
80 m_classNames.put(className, getNewClassName());
81 }
82 return m_classNames.get(className);
83 }
84
85 private String getNewClassName() {
86 return String.format("C%03d", m_classNames.size());
87 }
88 };
89
90 public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) {
91 this.namer = namer;
92
93 // stuff from the bytecode
94
95 this.classEntry = EntryFactory.getClassEntry(c);
96 this.fields = HashMultiset.create();
97 for (CtField field : c.getDeclaredFields()) {
98 this.fields.add(scrubType(field.getSignature()));
99 }
100 this.methods = HashMultiset.create();
101 for (CtMethod method : c.getDeclaredMethods()) {
102 this.methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method));
103 }
104 this.constructors = HashMultiset.create();
105 for (CtConstructor constructor : c.getDeclaredConstructors()) {
106 this.constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor));
107 }
108 this.staticInitializer = "";
109 if (c.getClassInitializer() != null) {
110 this.staticInitializer = getBehaviorSignature(c.getClassInitializer());
111 }
112 this.extendz = "";
113 if (c.getClassFile().getSuperclass() != null) {
114 this.extendz = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass()));
115 }
116 this.implementz = HashMultiset.create();
117 for (String interfaceName : c.getClassFile().getInterfaces()) {
118 this.implementz.add(scrubClassName(Descriptor.toJvmName(interfaceName)));
119 }
120
121 this.stringLiterals = Sets.newHashSet();
122 ConstPool constants = c.getClassFile().getConstPool();
123 for (int i = 1; i < constants.getSize(); i++) {
124 if (constants.getTag(i) == ConstPool.CONST_String) {
125 this.stringLiterals.add(constants.getStringInfo(i));
126 }
127 }
128
129 // stuff from the jar index
130
131 this.implementations = HashMultiset.create();
132 ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, this.classEntry);
133 if (implementationsNode != null) {
134 @SuppressWarnings("unchecked")
135 Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children();
136 while (implementations.hasMoreElements()) {
137 ClassImplementationsTreeNode node = implementations.nextElement();
138 this.implementations.add(scrubClassName(node.getClassEntry().getName()));
139 }
140 }
141
142 this.references = HashMultiset.create();
143 if (useReferences) {
144 for (CtField field : c.getDeclaredFields()) {
145 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
146 index.getFieldReferences(fieldEntry).forEach(this::addReference);
147 }
148 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
149 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
150 index.getBehaviorReferences(behaviorEntry).forEach(this::addReference);
151 }
152 }
153
154 this.outer = null;
155 if (this.classEntry.isInnerClass()) {
156 this.outer = this.classEntry.getOuterClassName();
157 }
158 }
159
160 private void addReference(EntryReference<? extends Entry, BehaviorEntry> reference) {
161 if (reference.context.getSignature() != null) {
162 this.references.add(String.format("%s_%s",
163 scrubClassName(reference.context.getClassName()),
164 scrubSignature(reference.context.getSignature())
165 ));
166 } else {
167 this.references.add(String.format("%s_<clinit>",
168 scrubClassName(reference.context.getClassName())
169 ));
170 }
171 }
172
173 public ClassEntry getClassEntry() {
174 return this.classEntry;
175 }
176
177 @Override
178 public String toString() {
179 StringBuilder buf = new StringBuilder();
180 buf.append("class: ");
181 buf.append(this.classEntry.getName());
182 buf.append(" ");
183 buf.append(hashCode());
184 buf.append("\n");
185 for (String field : this.fields) {
186 buf.append("\tfield ");
187 buf.append(field);
188 buf.append("\n");
189 }
190 for (String method : this.methods) {
191 buf.append("\tmethod ");
192 buf.append(method);
193 buf.append("\n");
194 }
195 for (String constructor : this.constructors) {
196 buf.append("\tconstructor ");
197 buf.append(constructor);
198 buf.append("\n");
199 }
200 if (this.staticInitializer.length() > 0) {
201 buf.append("\tinitializer ");
202 buf.append(this.staticInitializer);
203 buf.append("\n");
204 }
205 if (this.extendz.length() > 0) {
206 buf.append("\textends ");
207 buf.append(this.extendz);
208 buf.append("\n");
209 }
210 for (String interfaceName : this.implementz) {
211 buf.append("\timplements ");
212 buf.append(interfaceName);
213 buf.append("\n");
214 }
215 for (String implementation : this.implementations) {
216 buf.append("\timplemented by ");
217 buf.append(implementation);
218 buf.append("\n");
219 }
220 for (String reference : this.references) {
221 buf.append("\treference ");
222 buf.append(reference);
223 buf.append("\n");
224 }
225 buf.append("\touter ");
226 buf.append(this.outer);
227 buf.append("\n");
228 return buf.toString();
229 }
230
231 private String scrubClassName(String className) {
232 return m_classNameReplacer.replace(className);
233 }
234
235 private String scrubType(String typeName) {
236 return scrubType(new Type(typeName)).toString();
237 }
238
239 private Type scrubType(Type type) {
240 if (type.hasClass()) {
241 return new Type(type, m_classNameReplacer);
242 } else {
243 return type;
244 }
245 }
246
247 private String scrubSignature(String signature) {
248 return scrubSignature(new Signature(signature)).toString();
249 }
250
251 private Signature scrubSignature(Signature signature) {
252 return new Signature(signature, m_classNameReplacer);
253 }
254
255 private boolean isClassMatchedUniquely(String className) {
256 return this.namer != null && this.namer.getName(Descriptor.toJvmName(className)) != null;
257 }
258
259 private String getBehaviorSignature(CtBehavior behavior) {
260 try {
261 // does this method have an implementation?
262 if (behavior.getMethodInfo().getCodeAttribute() == null) {
263 return "(none)";
264 }
265
266 // compute the hash from the opcodes
267 ConstPool constants = behavior.getMethodInfo().getConstPool();
268 final MessageDigest digest = MessageDigest.getInstance("MD5");
269 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator();
270 while (iter.hasNext()) {
271 int pos = iter.next();
272
273 // update the hash with the opcode
274 int opcode = iter.byteAt(pos);
275 digest.update((byte) opcode);
276
277 switch (opcode) {
278 case Opcode.LDC: {
279 int constIndex = iter.byteAt(pos + 1);
280 updateHashWithConstant(digest, constants, constIndex);
281 }
282 break;
283
284 case Opcode.LDC_W:
285 case Opcode.LDC2_W: {
286 int constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2);
287 updateHashWithConstant(digest, constants, constIndex);
288 }
289 break;
290 }
291 }
292
293 // update hash with method and field accesses
294 behavior.instrument(new ExprEditor() {
295 @Override
296 public void edit(MethodCall call) {
297 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName())));
298 updateHashWithString(digest, scrubSignature(call.getSignature()));
299 if (isClassMatchedUniquely(call.getClassName())) {
300 updateHashWithString(digest, call.getMethodName());
301 }
302 }
303
304 @Override
305 public void edit(FieldAccess access) {
306 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName())));
307 updateHashWithString(digest, scrubType(access.getSignature()));
308 if (isClassMatchedUniquely(access.getClassName())) {
309 updateHashWithString(digest, access.getFieldName());
310 }
311 }
312
313 @Override
314 public void edit(ConstructorCall call) {
315 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName())));
316 updateHashWithString(digest, scrubSignature(call.getSignature()));
317 }
318
319 @Override
320 public void edit(NewExpr expr) {
321 updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName())));
322 }
323 });
324
325 // convert the hash to a hex string
326 return toHex(digest.digest());
327 } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) {
328 throw new Error(ex);
329 }
330 }
331
332 private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) {
333 ConstPoolEditor editor = new ConstPoolEditor(constants);
334 ConstInfoAccessor item = editor.getItem(index);
335 if (item.getType() == InfoType.StringInfo) {
336 updateHashWithString(digest, constants.getStringInfo(index));
337 }
338 // TODO: other constants
339 }
340
341 private void updateHashWithString(MessageDigest digest, String val) {
342 try {
343 digest.update(val.getBytes("UTF8"));
344 } catch (UnsupportedEncodingException ex) {
345 throw new Error(ex);
346 }
347 }
348
349 private String toHex(byte[] bytes) {
350 // function taken from:
351 // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java
352 final char[] hexArray = "0123456789ABCDEF".toCharArray();
353 char[] hexChars = new char[bytes.length * 2];
354 for (int j = 0; j < bytes.length; j++) {
355 int v = bytes[j] & 0xFF;
356 hexChars[j * 2] = hexArray[v >>> 4];
357 hexChars[j * 2 + 1] = hexArray[v & 0x0F];
358 }
359 return new String(hexChars);
360 }
361
362 @Override
363 public boolean equals(Object other) {
364 return other instanceof ClassIdentity && equals((ClassIdentity) other);
365 }
366
367 public boolean equals(ClassIdentity other) {
368 return this.fields.equals(other.fields)
369 && this.methods.equals(other.methods)
370 && this.constructors.equals(other.constructors)
371 && this.staticInitializer.equals(other.staticInitializer)
372 && this.extendz.equals(other.extendz)
373 && this.implementz.equals(other.implementz)
374 && this.implementations.equals(other.implementations)
375 && this.references.equals(other.references);
376 }
377
378 @Override
379 public int hashCode() {
380 List<Object> objs = Lists.newArrayList();
381 objs.addAll(this.fields);
382 objs.addAll(this.methods);
383 objs.addAll(this.constructors);
384 objs.add(this.staticInitializer);
385 objs.add(this.extendz);
386 objs.addAll(this.implementz);
387 objs.addAll(this.implementations);
388 objs.addAll(this.references);
389 return Util.combineHashesOrdered(objs);
390 }
391
392 public int getMatchScore(ClassIdentity other) {
393 return 2 * getNumMatches(this.extendz, other.extendz)
394 + 2 * getNumMatches(this.outer, other.outer)
395 + 2 * getNumMatches(this.implementz, other.implementz)
396 + getNumMatches(this.stringLiterals, other.stringLiterals)
397 + getNumMatches(this.fields, other.fields)
398 + getNumMatches(this.methods, other.methods)
399 + getNumMatches(this.constructors, other.constructors);
400 }
401
402 public int getMaxMatchScore() {
403 return 2 + 2 + 2 * this.implementz.size() + this.stringLiterals.size() + this.fields.size() + this.methods.size() + this.constructors.size();
404 }
405
406 public boolean matches(CtClass c) {
407 // just compare declaration counts
408 return this.fields.size() == c.getDeclaredFields().length
409 && this.methods.size() == c.getDeclaredMethods().length
410 && this.constructors.size() == c.getDeclaredConstructors().length;
411 }
412
413 private int getNumMatches(Set<String> a, Set<String> b) {
414 int numMatches = 0;
415 for (String val : a) {
416 if (b.contains(val)) {
417 numMatches++;
418 }
419 }
420 return numMatches;
421 }
422
423 private int getNumMatches(Multiset<String> a, Multiset<String> b) {
424 int numMatches = 0;
425 for (String val : a) {
426 if (b.contains(val)) {
427 numMatches++;
428 }
429 }
430 return numMatches;
431 }
432
433 private int getNumMatches(String a, String b) {
434 if (a == null && b == null) {
435 return 1;
436 } else if (a != null && b != null && a.equals(b)) {
437 return 1;
438 }
439 return 0;
440 }
441}
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 422529ec..00000000
--- a/src/main/java/cuchaz/enigma/convert/ClassMatch.java
+++ /dev/null
@@ -1,84 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.Sets;
14
15import java.util.Collection;
16import java.util.Set;
17
18import cuchaz.enigma.Util;
19import cuchaz.enigma.mapping.ClassEntry;
20
21
22public class ClassMatch {
23
24 public Set<ClassEntry> sourceClasses;
25 public Set<ClassEntry> destClasses;
26
27 public ClassMatch(Collection<ClassEntry> sourceClasses, Collection<ClassEntry> destClasses) {
28 this.sourceClasses = Sets.newHashSet(sourceClasses);
29 this.destClasses = Sets.newHashSet(destClasses);
30 }
31
32 public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) {
33 sourceClasses = Sets.newHashSet();
34 if (sourceClass != null) {
35 sourceClasses.add(sourceClass);
36 }
37 destClasses = Sets.newHashSet();
38 if (destClass != null) {
39 destClasses.add(destClass);
40 }
41 }
42
43 public boolean isMatched() {
44 return sourceClasses.size() > 0 && destClasses.size() > 0;
45 }
46
47 public boolean isAmbiguous() {
48 return sourceClasses.size() > 1 || destClasses.size() > 1;
49 }
50
51 public ClassEntry getUniqueSource() {
52 if (sourceClasses.size() != 1) {
53 throw new IllegalStateException("Match has ambiguous source!");
54 }
55 return sourceClasses.iterator().next();
56 }
57
58 public ClassEntry getUniqueDest() {
59 if (destClasses.size() != 1) {
60 throw new IllegalStateException("Match has ambiguous source!");
61 }
62 return destClasses.iterator().next();
63 }
64
65 public Set<ClassEntry> intersectSourceClasses(Set<ClassEntry> classes) {
66 Set<ClassEntry> intersection = Sets.newHashSet(sourceClasses);
67 intersection.retainAll(classes);
68 return intersection;
69 }
70
71 @Override
72 public int hashCode() {
73 return Util.combineHashesOrdered(sourceClasses, destClasses);
74 }
75
76 @Override
77 public boolean equals(Object other) {
78 return other instanceof ClassMatch && equals((ClassMatch) other);
79 }
80
81 public boolean equals(ClassMatch other) {
82 return this.sourceClasses.equals(other.sourceClasses) && this.destClasses.equals(other.destClasses);
83 }
84}
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 851c0829..00000000
--- a/src/main/java/cuchaz/enigma/convert/ClassMatches.java
+++ /dev/null
@@ -1,149 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.BiMap;
14import com.google.common.collect.HashBiMap;
15import com.google.common.collect.Maps;
16import com.google.common.collect.Sets;
17
18import java.util.*;
19
20import cuchaz.enigma.mapping.ClassEntry;
21
22
23public class ClassMatches implements Iterable<ClassMatch> {
24
25 Collection<ClassMatch> m_matches;
26 Map<ClassEntry, ClassMatch> m_matchesBySource;
27 Map<ClassEntry, ClassMatch> m_matchesByDest;
28 BiMap<ClassEntry, ClassEntry> m_uniqueMatches;
29 Map<ClassEntry, ClassMatch> m_ambiguousMatchesBySource;
30 Map<ClassEntry, ClassMatch> m_ambiguousMatchesByDest;
31 Set<ClassEntry> m_unmatchedSourceClasses;
32 Set<ClassEntry> m_unmatchedDestClasses;
33
34 public ClassMatches() {
35 this(new ArrayList<>());
36 }
37
38 public ClassMatches(Collection<ClassMatch> matches) {
39 m_matches = matches;
40 m_matchesBySource = Maps.newHashMap();
41 m_matchesByDest = Maps.newHashMap();
42 m_uniqueMatches = HashBiMap.create();
43 m_ambiguousMatchesBySource = Maps.newHashMap();
44 m_ambiguousMatchesByDest = Maps.newHashMap();
45 m_unmatchedSourceClasses = Sets.newHashSet();
46 m_unmatchedDestClasses = Sets.newHashSet();
47
48 matches.forEach(this::indexMatch);
49 }
50
51 public void add(ClassMatch match) {
52 m_matches.add(match);
53 indexMatch(match);
54 }
55
56 public void remove(ClassMatch match) {
57 for (ClassEntry sourceClass : match.sourceClasses) {
58 m_matchesBySource.remove(sourceClass);
59 m_uniqueMatches.remove(sourceClass);
60 m_ambiguousMatchesBySource.remove(sourceClass);
61 m_unmatchedSourceClasses.remove(sourceClass);
62 }
63 for (ClassEntry destClass : match.destClasses) {
64 m_matchesByDest.remove(destClass);
65 m_uniqueMatches.inverse().remove(destClass);
66 m_ambiguousMatchesByDest.remove(destClass);
67 m_unmatchedDestClasses.remove(destClass);
68 }
69 m_matches.remove(match);
70 }
71
72 public int size() {
73 return m_matches.size();
74 }
75
76 @Override
77 public Iterator<ClassMatch> iterator() {
78 return m_matches.iterator();
79 }
80
81 private void indexMatch(ClassMatch match) {
82 if (!match.isMatched()) {
83 // unmatched
84 m_unmatchedSourceClasses.addAll(match.sourceClasses);
85 m_unmatchedDestClasses.addAll(match.destClasses);
86 } else {
87 if (match.isAmbiguous()) {
88 // ambiguously matched
89 for (ClassEntry entry : match.sourceClasses) {
90 m_ambiguousMatchesBySource.put(entry, match);
91 }
92 for (ClassEntry entry : match.destClasses) {
93 m_ambiguousMatchesByDest.put(entry, match);
94 }
95 } else {
96 // uniquely matched
97 m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest());
98 }
99 }
100 for (ClassEntry entry : match.sourceClasses) {
101 m_matchesBySource.put(entry, match);
102 }
103 for (ClassEntry entry : match.destClasses) {
104 m_matchesByDest.put(entry, match);
105 }
106 }
107
108 public BiMap<ClassEntry, ClassEntry> getUniqueMatches() {
109 return m_uniqueMatches;
110 }
111
112 public Set<ClassEntry> getUnmatchedSourceClasses() {
113 return m_unmatchedSourceClasses;
114 }
115
116 public Set<ClassEntry> getUnmatchedDestClasses() {
117 return m_unmatchedDestClasses;
118 }
119
120 public Set<ClassEntry> getAmbiguouslyMatchedSourceClasses() {
121 return m_ambiguousMatchesBySource.keySet();
122 }
123
124 public ClassMatch getMatchBySource(ClassEntry sourceClass) {
125 return m_matchesBySource.get(sourceClass);
126 }
127
128 public void removeSource(ClassEntry sourceClass) {
129 ClassMatch match = m_matchesBySource.get(sourceClass);
130 if (match != null) {
131 remove(match);
132 match.sourceClasses.remove(sourceClass);
133 if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) {
134 add(match);
135 }
136 }
137 }
138
139 public void removeDest(ClassEntry destClass) {
140 ClassMatch match = m_matchesByDest.get(destClass);
141 if (match != null) {
142 remove(match);
143 match.destClasses.remove(destClass);
144 if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) {
145 add(match);
146 }
147 }
148 }
149}
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 194b6c4a..00000000
--- a/src/main/java/cuchaz/enigma/convert/ClassMatching.java
+++ /dev/null
@@ -1,146 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.BiMap;
14import com.google.common.collect.HashBiMap;
15import com.google.common.collect.Lists;
16import com.google.common.collect.Sets;
17
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.List;
21import java.util.Map.Entry;
22import java.util.Set;
23
24import cuchaz.enigma.mapping.ClassEntry;
25
26public class ClassMatching {
27
28 private ClassForest m_sourceClasses;
29 private ClassForest m_destClasses;
30 private BiMap<ClassEntry, ClassEntry> m_knownMatches;
31
32 public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) {
33 m_sourceClasses = new ClassForest(sourceIdentifier);
34 m_destClasses = new ClassForest(destIdentifier);
35 m_knownMatches = HashBiMap.create();
36 }
37
38 public void addKnownMatches(BiMap<ClassEntry, ClassEntry> knownMatches) {
39 m_knownMatches.putAll(knownMatches);
40 }
41
42 public void match(Iterable<ClassEntry> sourceClasses, Iterable<ClassEntry> destClasses) {
43 for (ClassEntry sourceClass : sourceClasses) {
44 if (!m_knownMatches.containsKey(sourceClass)) {
45 m_sourceClasses.add(sourceClass);
46 }
47 }
48 for (ClassEntry destClass : destClasses) {
49 if (!m_knownMatches.containsValue(destClass)) {
50 m_destClasses.add(destClass);
51 }
52 }
53 }
54
55 public Collection<ClassMatch> matches() {
56 List<ClassMatch> matches = Lists.newArrayList();
57 for (Entry<ClassEntry, ClassEntry> entry : m_knownMatches.entrySet()) {
58 matches.add(new ClassMatch(entry.getKey(), entry.getValue()));
59 }
60 for (ClassIdentity identity : m_sourceClasses.identities()) {
61 matches.add(new ClassMatch(m_sourceClasses.getClasses(identity), m_destClasses.getClasses(identity)));
62 }
63 for (ClassIdentity identity : m_destClasses.identities()) {
64 if (!m_sourceClasses.containsIdentity(identity)) {
65 matches.add(new ClassMatch(new ArrayList<>(), m_destClasses.getClasses(identity)));
66 }
67 }
68 return matches;
69 }
70
71 public Collection<ClassEntry> sourceClasses() {
72 Set<ClassEntry> classes = Sets.newHashSet();
73 for (ClassMatch match : matches()) {
74 classes.addAll(match.sourceClasses);
75 }
76 return classes;
77 }
78
79 public Collection<ClassEntry> destClasses() {
80 Set<ClassEntry> classes = Sets.newHashSet();
81 for (ClassMatch match : matches()) {
82 classes.addAll(match.destClasses);
83 }
84 return classes;
85 }
86
87 public BiMap<ClassEntry, ClassEntry> uniqueMatches() {
88 BiMap<ClassEntry, ClassEntry> uniqueMatches = HashBiMap.create();
89 for (ClassMatch match : matches()) {
90 if (match.isMatched() && !match.isAmbiguous()) {
91 uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest());
92 }
93 }
94 return uniqueMatches;
95 }
96
97 public Collection<ClassMatch> ambiguousMatches() {
98 List<ClassMatch> ambiguousMatches = Lists.newArrayList();
99 for (ClassMatch match : matches()) {
100 if (match.isMatched() && match.isAmbiguous()) {
101 ambiguousMatches.add(match);
102 }
103 }
104 return ambiguousMatches;
105 }
106
107 public Collection<ClassEntry> unmatchedSourceClasses() {
108 List<ClassEntry> classes = Lists.newArrayList();
109 for (ClassMatch match : matches()) {
110 if (!match.isMatched() && !match.sourceClasses.isEmpty()) {
111 classes.addAll(match.sourceClasses);
112 }
113 }
114 return classes;
115 }
116
117 public Collection<ClassEntry> unmatchedDestClasses() {
118 List<ClassEntry> classes = Lists.newArrayList();
119 for (ClassMatch match : matches()) {
120 if (!match.isMatched() && !match.destClasses.isEmpty()) {
121 classes.addAll(match.destClasses);
122 }
123 }
124 return classes;
125 }
126
127 @Override
128 public String toString() {
129
130 // count the ambiguous classes
131 int numAmbiguousSource = 0;
132 int numAmbiguousDest = 0;
133 for (ClassMatch match : ambiguousMatches()) {
134 numAmbiguousSource += match.sourceClasses.size();
135 numAmbiguousDest += match.destClasses.size();
136 }
137
138 StringBuilder buf = new StringBuilder();
139 buf.append(String.format("%20s%8s%8s\n", "", "Source", "Dest"));
140 buf.append(String.format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size()));
141 buf.append(String.format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size()));
142 buf.append(String.format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest));
143 buf.append(String.format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size()));
144 return buf.toString();
145 }
146}
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 e471c7dd..00000000
--- a/src/main/java/cuchaz/enigma/convert/ClassNamer.java
+++ /dev/null
@@ -1,56 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.BiMap;
14import com.google.common.collect.Maps;
15
16import java.util.Map;
17
18import cuchaz.enigma.mapping.ClassEntry;
19
20public class ClassNamer {
21
22 public interface SidedClassNamer {
23 String getName(String name);
24 }
25
26 private Map<String, String> sourceNames;
27 private Map<String, String> destNames;
28
29 public ClassNamer(BiMap<ClassEntry, ClassEntry> mappings) {
30 // convert the identity mappings to name maps
31 this.sourceNames = Maps.newHashMap();
32 this.destNames = Maps.newHashMap();
33 int i = 0;
34 for (Map.Entry<ClassEntry, ClassEntry> entry : mappings.entrySet()) {
35 String name = String.format("M%04d", i++);
36 this.sourceNames.put(entry.getKey().getName(), name);
37 this.destNames.put(entry.getValue().getName(), name);
38 }
39 }
40
41 public String getSourceName(String name) {
42 return this.sourceNames.get(name);
43 }
44
45 public String getDestName(String name) {
46 return this.destNames.get(name);
47 }
48
49 public SidedClassNamer getSourceNamer() {
50 return this::getSourceName;
51 }
52
53 public SidedClassNamer getDestNamer() {
54 return this::getDestName;
55 }
56}
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 7739adf2..00000000
--- a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
+++ /dev/null
@@ -1,540 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.*;
14
15import java.util.*;
16import java.util.jar.JarFile;
17
18import cuchaz.enigma.Constants;
19import cuchaz.enigma.Deobfuscator;
20import cuchaz.enigma.analysis.JarIndex;
21import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
22import cuchaz.enigma.mapping.*;
23
24public class MappingsConverter {
25
26 public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) {
27
28 // index jars
29 System.out.println("Indexing source jar...");
30 JarIndex sourceIndex = new JarIndex();
31 sourceIndex.indexJar(sourceJar, false);
32 System.out.println("Indexing dest jar...");
33 JarIndex destIndex = new JarIndex();
34 destIndex.indexJar(destJar, false);
35
36 // compute the matching
37 ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null);
38 return new ClassMatches(matching.matches());
39 }
40
41 public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) {
42
43 System.out.println("Iteratively matching classes");
44
45 ClassMatching lastMatching = null;
46 int round = 0;
47 SidedClassNamer sourceNamer = null;
48 SidedClassNamer destNamer = null;
49 for (boolean useReferences : Arrays.asList(false, true)) {
50
51 int numUniqueMatchesLastTime = 0;
52 if (lastMatching != null) {
53 numUniqueMatchesLastTime = lastMatching.uniqueMatches().size();
54 }
55
56 while (true) {
57
58 System.out.println("Round " + (++round) + "...");
59
60 // init the matching with identity settings
61 ClassMatching matching = new ClassMatching(
62 new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences),
63 new ClassIdentifier(destJar, destIndex, destNamer, useReferences)
64 );
65
66 if (knownMatches != null) {
67 matching.addKnownMatches(knownMatches);
68 }
69
70 if (lastMatching == null) {
71 // search all classes
72 matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries());
73 } else {
74 // we already know about these matches from last time
75 matching.addKnownMatches(lastMatching.uniqueMatches());
76
77 // search unmatched and ambiguously-matched classes
78 matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses());
79 for (ClassMatch match : lastMatching.ambiguousMatches()) {
80 matching.match(match.sourceClasses, match.destClasses);
81 }
82 }
83 System.out.println(matching);
84 BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches();
85
86 // did we match anything new this time?
87 if (uniqueMatches.size() > numUniqueMatchesLastTime) {
88 numUniqueMatchesLastTime = uniqueMatches.size();
89 lastMatching = matching;
90 } else {
91 break;
92 }
93
94 // update the namers
95 ClassNamer namer = new ClassNamer(uniqueMatches);
96 sourceNamer = namer.getSourceNamer();
97 destNamer = namer.getDestNamer();
98 }
99 }
100
101 return lastMatching;
102 }
103
104 public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
105
106 // sort the unique matches by size of inner class chain
107 Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create();
108 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) {
109 int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size();
110 matchesByDestChainSize.put(chainSize, match);
111 }
112
113 // build the mappings (in order of small-to-large inner chains)
114 Mappings newMappings = new Mappings();
115 List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet());
116 Collections.sort(chainSizes);
117 for (int chainSize : chainSizes) {
118 for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) {
119
120 // get class info
121 ClassEntry obfSourceClassEntry = match.getKey();
122 ClassEntry obfDestClassEntry = match.getValue();
123 List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry);
124
125 ClassMapping sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry);
126 if (sourceMapping == null) {
127 // if this class was never deobfuscated, don't try to match it
128 continue;
129 }
130
131 // find out where to make the dest class mapping
132 if (destClassChain.size() == 1) {
133 // not an inner class, add directly to mappings
134 newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false));
135 } else {
136 // inner class, find the outer class mapping
137 ClassMapping destMapping = null;
138 for (int i = 0; i < destClassChain.size() - 1; i++) {
139 ClassEntry destChainClassEntry = destClassChain.get(i);
140 if (destMapping == null) {
141 destMapping = newMappings.getClassByObf(destChainClassEntry);
142 if (destMapping == null) {
143 destMapping = new ClassMapping(destChainClassEntry.getName());
144 newMappings.addClassMapping(destMapping);
145 }
146 } else {
147 destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName());
148 if (destMapping == null) {
149 destMapping = new ClassMapping(destChainClassEntry.getName());
150 destMapping.addInnerClassMapping(destMapping);
151 }
152 }
153 }
154 if (destMapping != null) {
155 destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true));
156 }
157 }
158 }
159 }
160 return newMappings;
161 }
162
163 private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) {
164
165 ClassNameReplacer replacer = className -> {
166 ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className));
167 if (newClassEntry != null) {
168 return newClassEntry.getName();
169 }
170 return null;
171 };
172
173 ClassMapping newClassMapping;
174 String deobfName = oldClassMapping.getDeobfName();
175 if (deobfName != null) {
176 if (useSimpleName) {
177 deobfName = new ClassEntry(deobfName).getSimpleName();
178 }
179 newClassMapping = new ClassMapping(newObfClass.getName(), deobfName);
180 } else {
181 newClassMapping = new ClassMapping(newObfClass.getName());
182 }
183
184 // migrate fields
185 for (FieldMapping oldFieldMapping : oldClassMapping.fields()) {
186 if (canMigrate(oldFieldMapping.getObfType(), matches)) {
187 newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer));
188 } else {
189 System.out.println(String.format("Can't map field, dropping: %s.%s %s",
190 oldClassMapping.getDeobfName(),
191 oldFieldMapping.getDeobfName(),
192 oldFieldMapping.getObfType()
193 ));
194 }
195 }
196
197 // migrate methods
198 for (MethodMapping oldMethodMapping : oldClassMapping.methods()) {
199 if (canMigrate(oldMethodMapping.getObfSignature(), matches)) {
200 newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer));
201 } else {
202 System.out.println(String.format("Can't map method, dropping: %s.%s %s",
203 oldClassMapping.getDeobfName(),
204 oldMethodMapping.getDeobfName(),
205 oldMethodMapping.getObfSignature()
206 ));
207 }
208 }
209
210 return newClassMapping;
211 }
212
213 private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) {
214 for (Type oldObfType : oldObfSignature.types()) {
215 if (!canMigrate(oldObfType, classMatches)) {
216 return false;
217 }
218 }
219 return true;
220 }
221
222 private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) {
223
224 // non classes can be migrated
225 if (!oldObfType.hasClass()) {
226 return true;
227 }
228
229 // non obfuscated classes can be migrated
230 ClassEntry classEntry = oldObfType.getClassEntry();
231 if (!classEntry.getPackageName().equals(Constants.NONE_PACKAGE)) {
232 return true;
233 }
234
235 // obfuscated classes with mappings can be migrated
236 return classMatches.getUniqueMatches().containsKey(classEntry);
237 }
238
239 public interface Doer<T extends Entry> {
240 Collection<T> getDroppedEntries(MappingsChecker checker);
241
242 Collection<T> getObfEntries(JarIndex jarIndex);
243
244 Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping);
245
246 Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches);
247
248 void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry);
249
250 boolean hasObfMember(ClassMapping classMapping, T obfEntry);
251
252 void removeMemberByObf(ClassMapping classMapping, T obfEntry);
253 }
254
255 public static Doer<FieldEntry> getFieldDoer() {
256 return new Doer<FieldEntry>() {
257
258 @Override
259 public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) {
260 return checker.getDroppedFieldMappings().keySet();
261 }
262
263 @Override
264 public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) {
265 return jarIndex.getObfFieldEntries();
266 }
267
268 @Override
269 public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) {
270 return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields();
271 }
272
273 @Override
274 public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) {
275 Set<FieldEntry> out = Sets.newHashSet();
276 for (FieldEntry obfDestField : obfDestFields) {
277 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse());
278 if (translatedDestType.equals(obfSourceField.getType())) {
279 out.add(obfDestField);
280 }
281 }
282 return out;
283 }
284
285 @Override
286 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) {
287 FieldMapping fieldMapping = (FieldMapping) memberMapping;
288 classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType());
289 }
290
291 @Override
292 public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) {
293 return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null;
294 }
295
296 @Override
297 public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) {
298 classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType()));
299 }
300 };
301 }
302
303 public static Doer<BehaviorEntry> getMethodDoer() {
304 return new Doer<BehaviorEntry>() {
305
306 @Override
307 public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) {
308 return checker.getDroppedMethodMappings().keySet();
309 }
310
311 @Override
312 public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) {
313 return jarIndex.getObfBehaviorEntries();
314 }
315
316 @Override
317 public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) {
318 return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods();
319 }
320
321 @Override
322 public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) {
323 Set<BehaviorEntry> out = Sets.newHashSet();
324 for (BehaviorEntry obfDestField : obfDestFields) {
325 Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse());
326 if (translatedDestSignature == null && obfSourceField.getSignature() == null) {
327 out.add(obfDestField);
328 } else if (translatedDestSignature == null || obfSourceField.getSignature() == null) {
329 // skip it
330 } else if (translatedDestSignature.equals(obfSourceField.getSignature())) {
331 out.add(obfDestField);
332 }
333 }
334 return out;
335 }
336
337 @Override
338 public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) {
339 MethodMapping methodMapping = (MethodMapping) memberMapping;
340 classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature());
341 }
342
343 @Override
344 public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) {
345 return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null;
346 }
347
348 @Override
349 public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) {
350 classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()));
351 }
352 };
353 }
354
355 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) {
356
357 MemberMatches<T> memberMatches = new MemberMatches<>();
358
359 // unmatched source fields are easy
360 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
361 checker.dropBrokenMappings(destMappings);
362 for (T destObfEntry : doer.getDroppedEntries(checker)) {
363 T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse());
364 memberMatches.addUnmatchedSourceEntry(srcObfEntry);
365 }
366
367 // get matched fields (anything that's left after the checks/drops is matched(
368 for (ClassMapping classMapping : destMappings.classes()) {
369 collectMatchedFields(memberMatches, classMapping, classMatches, doer);
370 }
371
372 // get unmatched dest fields
373 for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) {
374 if (!memberMatches.isMatchedDestEntry(destEntry)) {
375 memberMatches.addUnmatchedDestEntry(destEntry);
376 }
377 }
378
379 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries...");
380
381 // go through the unmatched source fields and try to pick out the easy matches
382 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) {
383 for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) {
384
385 // get the possible dest matches
386 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
387
388 // filter by type/signature
389 Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches);
390
391 if (obfDestEntries.size() == 1) {
392 // make the easy match
393 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next());
394 } else if (obfDestEntries.isEmpty()) {
395 // no match is possible =(
396 memberMatches.makeSourceUnmatchable(obfSourceEntry);
397 }
398 }
399 }
400
401 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries",
402 memberMatches.getUnmatchedSourceEntries().size(),
403 memberMatches.getUnmatchableSourceEntries().size()
404 ));
405
406 return memberMatches;
407 }
408
409 private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) {
410
411 // get the fields for this class
412 for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) {
413 T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry());
414 T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
415 memberMatches.addMatch(srcObfField, destObfField);
416 }
417
418 // recurse
419 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) {
420 collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer);
421 }
422 }
423
424 @SuppressWarnings("unchecked")
425 private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) {
426 if (in instanceof FieldEntry) {
427 return (T) new FieldEntry(
428 map.get(in.getClassEntry()),
429 in.getName(),
430 translate(((FieldEntry) in).getType(), map)
431 );
432 } else if (in instanceof MethodEntry) {
433 return (T) new MethodEntry(
434 map.get(in.getClassEntry()),
435 in.getName(),
436 translate(((MethodEntry) in).getSignature(), map)
437 );
438 } else if (in instanceof ConstructorEntry) {
439 return (T) new ConstructorEntry(
440 map.get(in.getClassEntry()),
441 translate(((ConstructorEntry) in).getSignature(), map)
442 );
443 }
444 throw new Error("Unhandled entry type: " + in.getClass());
445 }
446
447 private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) {
448 return new Type(type, inClassName -> {
449 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
450 if (outClassEntry == null) {
451 return null;
452 }
453 return outClassEntry.getName();
454 });
455 }
456
457 private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) {
458 if (signature == null) {
459 return null;
460 }
461 return new Signature(signature, inClassName -> {
462 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
463 if (outClassEntry == null) {
464 return null;
465 }
466 return outClassEntry.getName();
467 });
468 }
469
470 public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) {
471 for (ClassMapping classMapping : mappings.classes()) {
472 applyMemberMatches(classMapping, classMatches, memberMatches, doer);
473 }
474 }
475
476 private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) {
477
478 // get the classes
479 ClassEntry obfDestClass = classMapping.getObfEntry();
480
481 // make a map of all the renames we need to make
482 Map<T, T> renames = Maps.newHashMap();
483 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) {
484 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass);
485 T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches);
486
487 // but drop the unmatchable things
488 if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) {
489 doer.removeMemberByObf(classMapping, obfOldDestEntry);
490 continue;
491 }
492
493 T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry);
494 if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) {
495 renames.put(obfOldDestEntry, obfNewDestEntry);
496 }
497 }
498
499 if (!renames.isEmpty()) {
500
501 // apply to this class (should never need more than n passes)
502 int numRenamesAppliedThisRound;
503 do {
504 numRenamesAppliedThisRound = 0;
505
506 for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) {
507 T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass);
508 T obfNewDestEntry = renames.get(obfOldDestEntry);
509 if (obfNewDestEntry != null) {
510 // make sure this rename won't cause a collision
511 // otherwise, save it for the next round and try again next time
512 if (!doer.hasObfMember(classMapping, obfNewDestEntry)) {
513 doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry);
514 renames.remove(obfOldDestEntry);
515 numRenamesAppliedThisRound++;
516 }
517 }
518 }
519 } while (numRenamesAppliedThisRound > 0);
520
521 if (!renames.isEmpty()) {
522 System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.",
523 classMapping.getObfFullName(), renames.size()
524 ));
525 for (Map.Entry<T, T> entry : renames.entrySet()) {
526 System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName()));
527 }
528 }
529 }
530
531 // recurse
532 for (ClassMapping innerClassMapping : classMapping.innerClasses()) {
533 applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer);
534 }
535 }
536
537 private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) {
538 return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse());
539 }
540}
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 f7853acf..00000000
--- a/src/main/java/cuchaz/enigma/convert/MatchesReader.java
+++ /dev/null
@@ -1,104 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.Lists;
14
15import java.io.BufferedReader;
16import java.io.File;
17import java.io.FileReader;
18import java.io.IOException;
19import java.util.Collection;
20import java.util.List;
21
22import cuchaz.enigma.mapping.*;
23
24
25public class MatchesReader {
26
27 public static ClassMatches readClasses(File file)
28 throws IOException {
29 try (BufferedReader in = new BufferedReader(new FileReader(file))) {
30 ClassMatches matches = new ClassMatches();
31 String line;
32 while ((line = in.readLine()) != null) {
33 matches.add(readClassMatch(line));
34 }
35 return matches;
36 }
37 }
38
39 private static ClassMatch readClassMatch(String line)
40 throws IOException {
41 String[] sides = line.split(":", 2);
42 return new ClassMatch(readClasses(sides[0]), readClasses(sides[1]));
43 }
44
45 private static Collection<ClassEntry> readClasses(String in) {
46 List<ClassEntry> entries = Lists.newArrayList();
47 for (String className : in.split(",")) {
48 className = className.trim();
49 if (className.length() > 0) {
50 entries.add(new ClassEntry(className));
51 }
52 }
53 return entries;
54 }
55
56 public static <T extends Entry> MemberMatches<T> readMembers(File file)
57 throws IOException {
58 try (BufferedReader in = new BufferedReader(new FileReader(file))) {
59 MemberMatches<T> matches = new MemberMatches<>();
60 String line;
61 while ((line = in.readLine()) != null) {
62 readMemberMatch(matches, line);
63 }
64 return matches;
65 }
66 }
67
68 private static <T extends Entry> void readMemberMatch(MemberMatches<T> matches, String line) {
69 if (line.startsWith("!")) {
70 T source = readEntry(line.substring(1));
71 matches.addUnmatchableSourceEntry(source);
72 } else {
73 String[] parts = line.split(":", 2);
74 T source = readEntry(parts[0]);
75 T dest = readEntry(parts[1]);
76 if (source != null && dest != null) {
77 matches.addMatch(source, dest);
78 } else if (source != null) {
79 matches.addUnmatchedSourceEntry(source);
80 } else if (dest != null) {
81 matches.addUnmatchedDestEntry(dest);
82 }
83 }
84 }
85
86 @SuppressWarnings("unchecked")
87 private static <T extends Entry> T readEntry(String in) {
88 if (in.length() <= 0) {
89 return null;
90 }
91 String[] parts = in.split(" ");
92 if (parts.length == 3 && parts[2].indexOf('(') < 0) {
93 return (T) new FieldEntry(new ClassEntry(parts[0]), parts[1], new Type(parts[2]));
94 } else {
95 if (parts.length == 2) {
96 return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1]);
97 } else if (parts.length == 3) {
98 return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]);
99 } else {
100 throw new Error("Malformed behavior entry: " + in);
101 }
102 }
103 }
104}
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 baf79293..00000000
--- a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java
+++ /dev/null
@@ -1,121 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import java.io.File;
14import java.io.FileWriter;
15import java.io.IOException;
16import java.util.Map;
17
18import cuchaz.enigma.mapping.BehaviorEntry;
19import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.Entry;
21import cuchaz.enigma.mapping.FieldEntry;
22
23
24public class MatchesWriter {
25
26 public static void writeClasses(ClassMatches matches, File file)
27 throws IOException {
28 try (FileWriter out = new FileWriter(file)) {
29 for (ClassMatch match : matches) {
30 writeClassMatch(out, match);
31 }
32 }
33 }
34
35 private static void writeClassMatch(FileWriter out, ClassMatch match)
36 throws IOException {
37 writeClasses(out, match.sourceClasses);
38 out.write(":");
39 writeClasses(out, match.destClasses);
40 out.write("\n");
41 }
42
43 private static void writeClasses(FileWriter out, Iterable<ClassEntry> classes)
44 throws IOException {
45 boolean isFirst = true;
46 for (ClassEntry entry : classes) {
47 if (isFirst) {
48 isFirst = false;
49 } else {
50 out.write(",");
51 }
52 out.write(entry.toString());
53 }
54 }
55
56 public static <T extends Entry> void writeMembers(MemberMatches<T> matches, File file)
57 throws IOException {
58 try (FileWriter out = new FileWriter(file)) {
59 for (Map.Entry<T, T> match : matches.matches().entrySet()) {
60 writeMemberMatch(out, match.getKey(), match.getValue());
61 }
62 for (T entry : matches.getUnmatchedSourceEntries()) {
63 writeMemberMatch(out, entry, null);
64 }
65 for (T entry : matches.getUnmatchedDestEntries()) {
66 writeMemberMatch(out, null, entry);
67 }
68 for (T entry : matches.getUnmatchableSourceEntries()) {
69 writeUnmatchableEntry(out, entry);
70 }
71 }
72 }
73
74 private static <T extends Entry> void writeMemberMatch(FileWriter out, T source, T dest)
75 throws IOException {
76 if (source != null) {
77 writeEntry(out, source);
78 }
79 out.write(":");
80 if (dest != null) {
81 writeEntry(out, dest);
82 }
83 out.write("\n");
84 }
85
86 private static <T extends Entry> void writeUnmatchableEntry(FileWriter out, T entry)
87 throws IOException {
88 out.write("!");
89 writeEntry(out, entry);
90 out.write("\n");
91 }
92
93 private static <T extends Entry> void writeEntry(FileWriter out, T entry)
94 throws IOException {
95 if (entry instanceof FieldEntry) {
96 writeField(out, (FieldEntry) entry);
97 } else if (entry instanceof BehaviorEntry) {
98 writeBehavior(out, (BehaviorEntry) entry);
99 }
100 }
101
102 private static void writeField(FileWriter out, FieldEntry fieldEntry)
103 throws IOException {
104 out.write(fieldEntry.getClassName());
105 out.write(" ");
106 out.write(fieldEntry.getName());
107 out.write(" ");
108 out.write(fieldEntry.getType().toString());
109 }
110
111 private static void writeBehavior(FileWriter out, BehaviorEntry behaviorEntry)
112 throws IOException {
113 out.write(behaviorEntry.getClassName());
114 out.write(" ");
115 out.write(behaviorEntry.getName());
116 out.write(" ");
117 if (behaviorEntry.getSignature() != null) {
118 out.write(behaviorEntry.getSignature().toString());
119 }
120 }
121}
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 662c1db9..00000000
--- a/src/main/java/cuchaz/enigma/convert/MemberMatches.java
+++ /dev/null
@@ -1,143 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import com.google.common.collect.*;
14
15import java.util.Collection;
16import java.util.Set;
17
18import cuchaz.enigma.mapping.ClassEntry;
19import cuchaz.enigma.mapping.Entry;
20
21
22public class MemberMatches<T extends Entry> {
23
24 private BiMap<T, T> m_matches;
25 private Multimap<ClassEntry, T> m_matchedSourceEntries;
26 private Multimap<ClassEntry, T> m_unmatchedSourceEntries;
27 private Multimap<ClassEntry, T> m_unmatchedDestEntries;
28 private Multimap<ClassEntry, T> m_unmatchableSourceEntries;
29
30 public MemberMatches() {
31 m_matches = HashBiMap.create();
32 m_matchedSourceEntries = HashMultimap.create();
33 m_unmatchedSourceEntries = HashMultimap.create();
34 m_unmatchedDestEntries = HashMultimap.create();
35 m_unmatchableSourceEntries = HashMultimap.create();
36 }
37
38 public void addMatch(T srcEntry, T destEntry) {
39 boolean wasAdded = m_matches.put(srcEntry, destEntry) == null;
40 assert (wasAdded);
41 wasAdded = m_matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry);
42 assert (wasAdded);
43 }
44
45 public void addUnmatchedSourceEntry(T sourceEntry) {
46 boolean wasAdded = m_unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry);
47 assert (wasAdded);
48 }
49
50 public void addUnmatchedDestEntry(T destEntry) {
51 boolean wasAdded = m_unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry);
52 assert (wasAdded);
53 }
54
55 public void addUnmatchableSourceEntry(T sourceEntry) {
56 boolean wasAdded = m_unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry);
57 assert (wasAdded);
58 }
59
60 public Set<ClassEntry> getSourceClassesWithUnmatchedEntries() {
61 return m_unmatchedSourceEntries.keySet();
62 }
63
64 public Collection<ClassEntry> getSourceClassesWithoutUnmatchedEntries() {
65 Set<ClassEntry> out = Sets.newHashSet();
66 out.addAll(m_matchedSourceEntries.keySet());
67 out.removeAll(m_unmatchedSourceEntries.keySet());
68 return out;
69 }
70
71 public Collection<T> getUnmatchedSourceEntries() {
72 return m_unmatchedSourceEntries.values();
73 }
74
75 public Collection<T> getUnmatchedSourceEntries(ClassEntry sourceClass) {
76 return m_unmatchedSourceEntries.get(sourceClass);
77 }
78
79 public Collection<T> getUnmatchedDestEntries() {
80 return m_unmatchedDestEntries.values();
81 }
82
83 public Collection<T> getUnmatchedDestEntries(ClassEntry destClass) {
84 return m_unmatchedDestEntries.get(destClass);
85 }
86
87 public Collection<T> getUnmatchableSourceEntries() {
88 return m_unmatchableSourceEntries.values();
89 }
90
91 public boolean hasSource(T sourceEntry) {
92 return m_matches.containsKey(sourceEntry) || m_unmatchedSourceEntries.containsValue(sourceEntry);
93 }
94
95 public boolean hasDest(T destEntry) {
96 return m_matches.containsValue(destEntry) || m_unmatchedDestEntries.containsValue(destEntry);
97 }
98
99 public BiMap<T, T> matches() {
100 return m_matches;
101 }
102
103 public boolean isMatchedSourceEntry(T sourceEntry) {
104 return m_matches.containsKey(sourceEntry);
105 }
106
107 public boolean isMatchedDestEntry(T destEntry) {
108 return m_matches.containsValue(destEntry);
109 }
110
111 public boolean isUnmatchableSourceEntry(T sourceEntry) {
112 return m_unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry);
113 }
114
115 public void makeMatch(T sourceEntry, T destEntry) {
116 boolean wasRemoved = m_unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
117 assert (wasRemoved);
118 wasRemoved = m_unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry);
119 assert (wasRemoved);
120 addMatch(sourceEntry, destEntry);
121 }
122
123 public boolean isMatched(T sourceEntry, T destEntry) {
124 T match = m_matches.get(sourceEntry);
125 return match != null && match.equals(destEntry);
126 }
127
128 public void unmakeMatch(T sourceEntry, T destEntry) {
129 boolean wasRemoved = m_matches.remove(sourceEntry) != null;
130 assert (wasRemoved);
131 wasRemoved = m_matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
132 assert (wasRemoved);
133 addUnmatchedSourceEntry(sourceEntry);
134 addUnmatchedDestEntry(destEntry);
135 }
136
137 public void makeSourceUnmatchable(T sourceEntry) {
138 assert (!isMatchedSourceEntry(sourceEntry));
139 boolean wasRemoved = m_unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
140 assert (wasRemoved);
141 addUnmatchableSourceEntry(sourceEntry);
142 }
143}
diff --git a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
index a75db366..f58d0129 100644
--- a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
+++ b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
@@ -15,8 +15,6 @@ import javax.swing.text.Highlighter;
15 15
16public class BrowserCaret extends DefaultCaret { 16public class BrowserCaret extends DefaultCaret {
17 17
18 private static final long serialVersionUID = 1158977422507969940L;
19
20 private static final Highlighter.HighlightPainter selectionPainter = (g, p0, p1, bounds, c) -> { 18 private static final Highlighter.HighlightPainter selectionPainter = (g, p0, p1, bounds, c) -> {
21 }; 19 };
22 20
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 e8136881..00000000
--- a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
+++ /dev/null
@@ -1,521 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import com.google.common.collect.BiMap;
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16
17import java.awt.BorderLayout;
18import java.awt.Container;
19import java.awt.Dimension;
20import java.awt.FlowLayout;
21import java.awt.event.ActionListener;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.List;
25import java.util.Map;
26
27import javax.swing.*;
28
29import cuchaz.enigma.Constants;
30import cuchaz.enigma.Deobfuscator;
31import cuchaz.enigma.convert.*;
32import cuchaz.enigma.mapping.ClassEntry;
33import cuchaz.enigma.mapping.Mappings;
34import cuchaz.enigma.mapping.MappingsChecker;
35import de.sciss.syntaxpane.DefaultSyntaxKit;
36
37
38public class ClassMatchingGui {
39
40 private enum SourceType {
41 Matched {
42 @Override
43 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
44 return matches.getUniqueMatches().keySet();
45 }
46 },
47 Unmatched {
48 @Override
49 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
50 return matches.getUnmatchedSourceClasses();
51 }
52 },
53 Ambiguous {
54 @Override
55 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
56 return matches.getAmbiguouslyMatchedSourceClasses();
57 }
58 };
59
60 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
61 JRadioButton button = new JRadioButton(name(), this == getDefault());
62 button.setActionCommand(name());
63 button.addActionListener(listener);
64 group.add(button);
65 return button;
66 }
67
68 public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches);
69
70 public static SourceType getDefault() {
71 return values()[0];
72 }
73 }
74
75 public interface SaveListener {
76 void save(ClassMatches matches);
77 }
78
79 // controls
80 private JFrame frame;
81 private ClassSelector sourceClasses;
82 private ClassSelector destClasses;
83 private CodeReader sourceReader;
84 private CodeReader destReader;
85 private JLabel sourceClassLabel;
86 private JLabel destClassLabel;
87 private JButton matchButton;
88 private Map<SourceType, JRadioButton> sourceTypeButtons;
89 private JCheckBox advanceCheck;
90 private JCheckBox top10Matches;
91
92 private ClassMatches classMatches;
93 private Deobfuscator sourceDeobfuscator;
94 private Deobfuscator destDeobfuscator;
95 private ClassEntry sourceClass;
96 private ClassEntry destClass;
97 private SourceType sourceType;
98 private SaveListener saveListener;
99
100 public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
101
102 this.classMatches = matches;
103 this.sourceDeobfuscator = sourceDeobfuscator;
104 this.destDeobfuscator = destDeobfuscator;
105
106 // init frame
107 this.frame = new JFrame(Constants.NAME + " - Class Matcher");
108 final Container pane = this.frame.getContentPane();
109 pane.setLayout(new BorderLayout());
110
111 // init source side
112 JPanel sourcePanel = new JPanel();
113 sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS));
114 sourcePanel.setPreferredSize(new Dimension(200, 0));
115 pane.add(sourcePanel, BorderLayout.WEST);
116 sourcePanel.add(new JLabel("Source Classes"));
117
118 // init source type radios
119 JPanel sourceTypePanel = new JPanel();
120 sourcePanel.add(sourceTypePanel);
121 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
122 ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand()));
123 ButtonGroup sourceTypeButtons = new ButtonGroup();
124 this.sourceTypeButtons = Maps.newHashMap();
125 for (SourceType sourceType : SourceType.values()) {
126 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
127 this.sourceTypeButtons.put(sourceType, button);
128 sourceTypePanel.add(button);
129 }
130
131 this.sourceClasses = new ClassSelector(ClassSelector.DEOBF_CLASS_COMPARATOR);
132 this.sourceClasses.setListener(this::setSourceClass);
133 JScrollPane sourceScroller = new JScrollPane(this.sourceClasses);
134 sourcePanel.add(sourceScroller);
135
136 // init dest side
137 JPanel destPanel = new JPanel();
138 destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS));
139 destPanel.setPreferredSize(new Dimension(200, 0));
140 pane.add(destPanel, BorderLayout.WEST);
141 destPanel.add(new JLabel("Destination Classes"));
142
143 this.top10Matches = new JCheckBox("Show only top 10 matches");
144 destPanel.add(this.top10Matches);
145 this.top10Matches.addActionListener(event -> toggleTop10Matches());
146
147 this.destClasses = new ClassSelector(ClassSelector.DEOBF_CLASS_COMPARATOR);
148 this.destClasses.setListener(this::setDestClass);
149 JScrollPane destScroller = new JScrollPane(this.destClasses);
150 destPanel.add(destScroller);
151
152 JButton autoMatchButton = new JButton("AutoMatch");
153 autoMatchButton.addActionListener(event -> autoMatch());
154 destPanel.add(autoMatchButton);
155
156 // init source panels
157 DefaultSyntaxKit.initKit();
158 this.sourceReader = new CodeReader();
159 this.destReader = new CodeReader();
160
161 // init all the splits
162 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(this.sourceReader));
163 splitLeft.setResizeWeight(0); // let the right side take all the slack
164 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(this.destReader), destPanel);
165 splitRight.setResizeWeight(1); // let the left side take all the slack
166 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight);
167 splitCenter.setResizeWeight(0.5); // resize 50:50
168 pane.add(splitCenter, BorderLayout.CENTER);
169 splitCenter.resetToPreferredSizes();
170
171 // init bottom panel
172 JPanel bottomPanel = new JPanel();
173 bottomPanel.setLayout(new FlowLayout());
174
175 this.sourceClassLabel = new JLabel();
176 this.sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT);
177 this.destClassLabel = new JLabel();
178 this.destClassLabel.setHorizontalAlignment(SwingConstants.LEFT);
179
180 this.matchButton = new JButton();
181
182 this.advanceCheck = new JCheckBox("Advance to next likely match");
183 this.advanceCheck.addActionListener(event -> {
184 if (this.advanceCheck.isSelected()) {
185 advance();
186 }
187 });
188
189 bottomPanel.add(this.sourceClassLabel);
190 bottomPanel.add(this.matchButton);
191 bottomPanel.add(this.destClassLabel);
192 bottomPanel.add(this.advanceCheck);
193 pane.add(bottomPanel, BorderLayout.SOUTH);
194
195 // show the frame
196 pane.doLayout();
197 this.frame.setSize(1024, 576);
198 this.frame.setMinimumSize(new Dimension(640, 480));
199 this.frame.setVisible(true);
200 this.frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
201
202 // init state
203 updateDestMappings();
204 setSourceType(SourceType.getDefault());
205 updateMatchButton();
206 this.saveListener = null;
207 }
208
209 public void setSaveListener(SaveListener val) {
210 this.saveListener = val;
211 }
212
213 private void updateDestMappings() {
214
215 Mappings newMappings = MappingsConverter.newMappings(this.classMatches, this.sourceDeobfuscator.getMappings(), this.sourceDeobfuscator, this.destDeobfuscator);
216
217 // look for dropped mappings
218 MappingsChecker checker = new MappingsChecker(this.destDeobfuscator.getJarIndex());
219 checker.dropBrokenMappings(newMappings);
220
221 // count them
222 int numDroppedFields = checker.getDroppedFieldMappings().size();
223 int numDroppedMethods = checker.getDroppedMethodMappings().size();
224 System.out.println(String.format("%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods",
225 numDroppedFields + numDroppedMethods,
226 numDroppedFields,
227 numDroppedMethods
228 ));
229
230 this.destDeobfuscator.setMappings(newMappings);
231 }
232
233 protected void setSourceType(SourceType val) {
234
235 // show the source classes
236 this.sourceType = val;
237 this.sourceClasses.setClasses(deobfuscateClasses(this.sourceType.getSourceClasses(this.classMatches), this.sourceDeobfuscator));
238
239 // update counts
240 for (SourceType sourceType : SourceType.values()) {
241 this.sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
242 sourceType.name(),
243 sourceType.getSourceClasses(this.classMatches).size()
244 ));
245 }
246 }
247
248 private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) {
249 List<ClassEntry> out = Lists.newArrayList();
250 for (ClassEntry entry : in) {
251
252 ClassEntry deobf = deobfuscator.deobfuscateEntry(entry);
253
254 // make sure we preserve any scores
255 if (entry instanceof ScoredClassEntry) {
256 deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore());
257 }
258
259 out.add(deobf);
260 }
261 return out;
262 }
263
264 protected void setSourceClass(ClassEntry classEntry) {
265
266 Runnable onGetDestClasses = null;
267 if (this.advanceCheck.isSelected()) {
268 onGetDestClasses = this::pickBestDestClass;
269 }
270
271 setSourceClass(classEntry, onGetDestClasses);
272 }
273
274 protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) {
275
276 // update the current source class
277 this.sourceClass = classEntry;
278 this.sourceClassLabel.setText(this.sourceClass != null ? this.sourceClass.getName() : "");
279
280 if (this.sourceClass != null) {
281
282 // show the dest class(es)
283 ClassMatch match = this.classMatches.getMatchBySource(this.sourceDeobfuscator.obfuscateEntry(this.sourceClass));
284 assert (match != null);
285 if (match.destClasses.isEmpty()) {
286
287 this.destClasses.setClasses(null);
288
289 // run in a separate thread to keep ui responsive
290 new Thread() {
291 @Override
292 public void run() {
293 destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator));
294 destClasses.expandAll();
295
296 if (onGetDestClasses != null) {
297 onGetDestClasses.run();
298 }
299 }
300 }.start();
301
302 } else {
303
304 this.destClasses.setClasses(deobfuscateClasses(match.destClasses, this.destDeobfuscator));
305 this.destClasses.expandAll();
306
307 if (onGetDestClasses != null) {
308 onGetDestClasses.run();
309 }
310 }
311 }
312
313 setDestClass(null);
314 this.sourceReader.decompileClass(this.sourceClass, this.sourceDeobfuscator, () -> this.sourceReader.navigateToClassDeclaration(this.sourceClass));
315
316 updateMatchButton();
317 }
318
319 private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) {
320
321 ClassEntry obfSourceClass = this.sourceDeobfuscator.obfuscateEntry(sourceClass);
322
323 // set up identifiers
324 ClassNamer namer = new ClassNamer(this.classMatches.getUniqueMatches());
325 ClassIdentifier sourceIdentifier = new ClassIdentifier(this.sourceDeobfuscator.getJar(), this.sourceDeobfuscator.getJarIndex(), namer.getSourceNamer(), true);
326 ClassIdentifier destIdentifier = new ClassIdentifier(this.destDeobfuscator.getJar(), this.destDeobfuscator.getJarIndex(), namer.getDestNamer(), true);
327
328 try {
329
330 // rank all the unmatched dest classes against the source class
331 ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass);
332 List<ClassEntry> scoredDestClasses = Lists.newArrayList();
333 for (ClassEntry unmatchedDestClass : this.classMatches.getUnmatchedDestClasses()) {
334 ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass);
335 float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity))
336 / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore());
337 scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score));
338 }
339
340 if (this.top10Matches.isSelected() && scoredDestClasses.size() > 10) {
341 Collections.sort(scoredDestClasses, (a, b) -> {
342 ScoredClassEntry sa = (ScoredClassEntry) a;
343 ScoredClassEntry sb = (ScoredClassEntry) b;
344 return -Float.compare(sa.getScore(), sb.getScore());
345 });
346 scoredDestClasses = scoredDestClasses.subList(0, 10);
347 }
348
349 return scoredDestClasses;
350
351 } catch (ClassNotFoundException ex) {
352 throw new Error("Unable to find class " + ex.getMessage());
353 }
354 }
355
356 protected void setDestClass(ClassEntry classEntry) {
357
358 // update the current source class
359 this.destClass = classEntry;
360 this.destClassLabel.setText(this.destClass != null ? this.destClass.getName() : "");
361
362 this.destReader.decompileClass(this.destClass, this.destDeobfuscator, () -> this.destReader.navigateToClassDeclaration(this.destClass));
363
364 updateMatchButton();
365 }
366
367 private void updateMatchButton() {
368
369 ClassEntry obfSource = this.sourceDeobfuscator.obfuscateEntry(this.sourceClass);
370 ClassEntry obfDest = this.destDeobfuscator.obfuscateEntry(this.destClass);
371
372 BiMap<ClassEntry, ClassEntry> uniqueMatches = this.classMatches.getUniqueMatches();
373 boolean twoSelected = this.sourceClass != null && this.destClass != null;
374 boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest);
375 boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest);
376
377 GuiTricks.deactivateButton(this.matchButton);
378 if (twoSelected) {
379 if (isMatched) {
380 GuiTricks.activateButton(this.matchButton, "Unmatch", event -> onUnmatchClick());
381 } else if (canMatch) {
382 GuiTricks.activateButton(this.matchButton, "Match", event -> onMatchClick());
383 }
384 }
385 }
386
387 private void onMatchClick() {
388 // precondition: source and dest classes are set correctly
389
390 ClassEntry obfSource = this.sourceDeobfuscator.obfuscateEntry(this.sourceClass);
391 ClassEntry obfDest = this.destDeobfuscator.obfuscateEntry(this.destClass);
392
393 // remove the classes from their match
394 this.classMatches.removeSource(obfSource);
395 this.classMatches.removeDest(obfDest);
396
397 // add them as matched classes
398 this.classMatches.add(new ClassMatch(obfSource, obfDest));
399
400 ClassEntry nextClass = null;
401 if (this.advanceCheck.isSelected()) {
402 nextClass = this.sourceClasses.getNextClass(this.sourceClass);
403 }
404
405 save();
406 updateMatches();
407
408 if (nextClass != null) {
409 advance(nextClass);
410 }
411 }
412
413 private void onUnmatchClick() {
414 // precondition: source and dest classes are set to a unique match
415
416 ClassEntry obfSource = this.sourceDeobfuscator.obfuscateEntry(this.sourceClass);
417
418 // remove the source to break the match, then add the source back as unmatched
419 this.classMatches.removeSource(obfSource);
420 this.classMatches.add(new ClassMatch(obfSource, null));
421
422 save();
423 updateMatches();
424 }
425
426 private void updateMatches() {
427 updateDestMappings();
428 setDestClass(null);
429 this.destClasses.setClasses(null);
430 updateMatchButton();
431
432 // remember where we were in the source tree
433 String packageName = this.sourceClasses.getSelectedPackage();
434
435 setSourceType(this.sourceType);
436
437 this.sourceClasses.expandPackage(packageName);
438 }
439
440 private void save() {
441 if (this.saveListener != null) {
442 this.saveListener.save(this.classMatches);
443 }
444 }
445
446 private void autoMatch() {
447
448 System.out.println("Automatching...");
449
450 // compute a new matching
451 ClassMatching matching = MappingsConverter.computeMatching(this.sourceDeobfuscator.getJar(), this.sourceDeobfuscator.getJarIndex(),
452 this.destDeobfuscator.getJar(), this.destDeobfuscator.getJarIndex(), this.classMatches.getUniqueMatches());
453 ClassMatches newMatches = new ClassMatches(matching.matches());
454 System.out.println(String.format("Automatch found %d new matches", newMatches.getUniqueMatches().size() - this.classMatches.getUniqueMatches().size()));
455
456 // update the current matches
457 this.classMatches = newMatches;
458 save();
459 updateMatches();
460 }
461
462 private void advance() {
463 advance(null);
464 }
465
466 private void advance(ClassEntry sourceClass) {
467
468 // make sure we have a source class
469 if (sourceClass == null) {
470 sourceClass = this.sourceClasses.getSelectedClass();
471 if (sourceClass != null) {
472 sourceClass = this.sourceClasses.getNextClass(sourceClass);
473 } else {
474 sourceClass = this.sourceClasses.getFirstClass();
475 }
476 }
477
478 // set the source class
479 setSourceClass(sourceClass, this::pickBestDestClass);
480 this.sourceClasses.setSelectionClass(sourceClass);
481 }
482
483 private void pickBestDestClass() {
484
485 // then, pick the best dest class
486 ClassEntry firstClass = null;
487 ScoredClassEntry bestDestClass = null;
488 for (ClassSelectorPackageNode packageNode : this.destClasses.packageNodes()) {
489 for (ClassSelectorClassNode classNode : this.destClasses.classNodes(packageNode)) {
490 if (firstClass == null) {
491 firstClass = classNode.getClassEntry();
492 }
493 if (classNode.getClassEntry() instanceof ScoredClassEntry) {
494 ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry();
495 if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) {
496 bestDestClass = scoredClass;
497 }
498 }
499 }
500 }
501
502 // pick the entry to show
503 ClassEntry destClass = null;
504 if (bestDestClass != null) {
505 destClass = bestDestClass;
506 } else if (firstClass != null) {
507 destClass = firstClass;
508 }
509
510 setDestClass(destClass);
511 this.destClasses.setSelectionClass(destClass);
512 }
513
514 private void toggleTop10Matches() {
515 if (this.sourceClass != null) {
516 this.destClasses.clearSelection();
517 this.destClasses.setClasses(deobfuscateClasses(getLikelyMatches(this.sourceClass), this.destDeobfuscator));
518 this.destClasses.expandAll();
519 }
520 }
521}
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
index 2ee3b2ae..27b4d360 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
@@ -24,17 +24,13 @@ import javax.swing.tree.DefaultMutableTreeNode;
24import javax.swing.tree.DefaultTreeModel; 24import javax.swing.tree.DefaultTreeModel;
25import javax.swing.tree.TreePath; 25import javax.swing.tree.TreePath;
26 26
27import cuchaz.enigma.gui.node.ClassSelectorClassNode;
28import cuchaz.enigma.gui.node.ClassSelectorPackageNode;
27import cuchaz.enigma.mapping.ClassEntry; 29import cuchaz.enigma.mapping.ClassEntry;
28 30
29public class ClassSelector extends JTree { 31public class ClassSelector extends JTree {
30 32
31 private static final long serialVersionUID = -7632046902384775977L; 33 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = (a, b) -> a.getName().compareTo(b.getName());
32 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = (a, b) -> {
33 if (a instanceof ScoredClassEntry && b instanceof ScoredClassEntry) {
34 return Float.compare(((ScoredClassEntry) b).getScore(), ((ScoredClassEntry) a).getScore());
35 }
36 return a.getName().compareTo(b.getName());
37 };
38 34
39 public interface ClassSelectionListener { 35 public interface ClassSelectionListener {
40 void onSelectClass(ClassEntry classEntry); 36 void onSelectClass(ClassEntry classEntry);
@@ -75,6 +71,7 @@ public class ClassSelector extends JTree {
75 } 71 }
76 72
77 public void setClasses(Collection<ClassEntry> classEntries) { 73 public void setClasses(Collection<ClassEntry> classEntries) {
74 String state = getExpansionState(this, 0);
78 if (classEntries == null) { 75 if (classEntries == null) {
79 setModel(null); 76 setModel(null);
80 return; 77 return;
@@ -137,125 +134,45 @@ public class ClassSelector extends JTree {
137 134
138 // finally, update the tree control 135 // finally, update the tree control
139 setModel(new DefaultTreeModel(root)); 136 setModel(new DefaultTreeModel(root));
140 }
141
142 public ClassEntry getSelectedClass() {
143 if (!isSelectionEmpty()) {
144 Object selectedNode = getSelectionPath().getLastPathComponent();
145 if (selectedNode instanceof ClassSelectorClassNode) {
146 ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode;
147 return classNode.getClassEntry();
148 }
149 }
150 return null;
151 }
152
153 public String getSelectedPackage() {
154 if (!isSelectionEmpty()) {
155 Object selectedNode = getSelectionPath().getLastPathComponent();
156 if (selectedNode instanceof ClassSelectorPackageNode) {
157 ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) selectedNode;
158 return packageNode.getPackageName();
159 } else if (selectedNode instanceof ClassSelectorClassNode) {
160 ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode;
161 return classNode.getClassEntry().getPackageName();
162 }
163 }
164 return null;
165 }
166
167 public Iterable<ClassSelectorPackageNode> packageNodes() {
168 List<ClassSelectorPackageNode> nodes = Lists.newArrayList();
169 DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();
170 Enumeration<?> children = root.children();
171 while (children.hasMoreElements()) {
172 ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) children.nextElement();
173 nodes.add(packageNode);
174 }
175 return nodes;
176 }
177 137
178 public Iterable<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) { 138 restoreExpanstionState(this, 0, state);
179 List<ClassSelectorClassNode> nodes = Lists.newArrayList();
180 Enumeration<?> children = packageNode.children();
181 while (children.hasMoreElements()) {
182 ClassSelectorClassNode classNode = (ClassSelectorClassNode) children.nextElement();
183 nodes.add(classNode);
184 }
185 return nodes;
186 } 139 }
187 140
188 public void expandPackage(String packageName) { 141 public boolean isDescendant(TreePath path1, TreePath path2) {
189 if (packageName == null) { 142 int count1 = path1.getPathCount();
190 return; 143 int count2 = path2.getPathCount();
144 if (count1 <= count2) {
145 return false;
191 } 146 }
192 for (ClassSelectorPackageNode packageNode : packageNodes()) { 147 while (count1 != count2) {
193 if (packageNode.getPackageName().equals(packageName)) { 148 path1 = path1.getParentPath();
194 expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode})); 149 count1--;
195 return;
196 }
197 } 150 }
151 return path1.equals(path2);
198 } 152 }
199 153
200 public void expandAll() { 154 public String getExpansionState(JTree tree, int row) {
201 for (ClassSelectorPackageNode packageNode : packageNodes()) { 155 TreePath rowPath = tree.getPathForRow(row);
202 expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode})); 156 StringBuffer buf = new StringBuffer();
203 } 157 int rowCount = tree.getRowCount();
204 } 158 for (int i = row; i < rowCount; i++) {
205 159 TreePath path = tree.getPathForRow(i);
206 public ClassEntry getFirstClass() { 160 if (i == row || isDescendant(path, rowPath)) {
207 for (ClassSelectorPackageNode packageNode : packageNodes()) { 161 if (tree.isExpanded(path)) {
208 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 162 buf.append("," + String.valueOf(i - row));
209 return classNode.getClassEntry();
210 }
211 }
212 return null;
213 }
214
215 public ClassSelectorPackageNode getPackageNode(ClassEntry entry) {
216 for (ClassSelectorPackageNode packageNode : packageNodes()) {
217 if (packageNode.getPackageName().equals(entry.getPackageName())) {
218 return packageNode;
219 }
220 }
221 return null;
222 }
223
224 public ClassEntry getNextClass(ClassEntry entry) {
225 boolean foundIt = false;
226 for (ClassSelectorPackageNode packageNode : packageNodes()) {
227 if (!foundIt) {
228 // skip to the package with our target in it
229 if (packageNode.getPackageName().equals(entry.getPackageName())) {
230 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
231 if (!foundIt) {
232 if (classNode.getClassEntry().equals(entry)) {
233 foundIt = true;
234 }
235 } else {
236 // return the next class
237 return classNode.getClassEntry();
238 }
239 }
240 } 163 }
241 } else { 164 } else {
242 // return the next class 165 break;
243 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
244 return classNode.getClassEntry();
245 }
246 } 166 }
247 } 167 }
248 return null; 168 return buf.toString();
249 } 169 }
250 170
251 public void setSelectionClass(ClassEntry classEntry) { 171 public void restoreExpanstionState(JTree tree, int row, String expansionState) {
252 expandPackage(classEntry.getPackageName()); 172 StringTokenizer stok = new StringTokenizer(expansionState, ",");
253 for (ClassSelectorPackageNode packageNode : packageNodes()) { 173 while (stok.hasMoreTokens()) {
254 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 174 int token = row + Integer.parseInt(stok.nextToken());
255 if (classNode.getClassEntry().equals(classEntry)) { 175 tree.expandRow(token);
256 setSelectionPath(new TreePath(new Object[]{getModel().getRoot(), packageNode, classNode}));
257 }
258 }
259 } 176 }
260 } 177 }
261} 178}
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java
deleted file mode 100644
index a476fa51..00000000
--- a/src/main/java/cuchaz/enigma/gui/CodeReader.java
+++ /dev/null
@@ -1,202 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import com.strobel.decompiler.languages.java.ast.CompilationUnit;
14
15import java.awt.Rectangle;
16import java.awt.event.ActionEvent;
17import java.awt.event.ActionListener;
18
19import javax.swing.JEditorPane;
20import javax.swing.SwingUtilities;
21import javax.swing.Timer;
22import javax.swing.text.BadLocationException;
23import javax.swing.text.Highlighter.HighlightPainter;
24
25import cuchaz.enigma.Deobfuscator;
26import cuchaz.enigma.analysis.EntryReference;
27import cuchaz.enigma.analysis.SourceIndex;
28import cuchaz.enigma.analysis.Token;
29import cuchaz.enigma.mapping.ClassEntry;
30import cuchaz.enigma.mapping.Entry;
31import de.sciss.syntaxpane.DefaultSyntaxKit;
32
33
34public class CodeReader extends JEditorPane {
35
36 private static final long serialVersionUID = 3673180950485748810L;
37
38 private static final Object lock = new Object();
39
40 public interface SelectionListener {
41 void onSelect(EntryReference<Entry, Entry> reference);
42 }
43
44 private SelectionHighlightPainter selectionHighlightPainter;
45 private SourceIndex sourceIndex;
46 private SelectionListener selectionListener;
47
48 public CodeReader() {
49
50 setEditable(false);
51 setContentType("text/java");
52
53 // turn off token highlighting (it's wrong most of the time anyway...)
54 DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit();
55 kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker");
56
57 // hook events
58 addCaretListener(event -> {
59 if (this.selectionListener != null && this.sourceIndex != null) {
60 Token token = this.sourceIndex.getReferenceToken(event.getDot());
61 if (token != null) {
62 this.selectionListener.onSelect(this.sourceIndex.getDeobfReference(token));
63 } else {
64 this.selectionListener.onSelect(null);
65 }
66 }
67 });
68
69 this.selectionHighlightPainter = new SelectionHighlightPainter();
70 this.sourceIndex = null;
71 this.selectionListener = null;
72 }
73
74 public void setSelectionListener(SelectionListener val) {
75 this.selectionListener = val;
76 }
77
78 public void setCode(String code) {
79 // sadly, the java lexer is not thread safe, so we have to serialize all these calls
80 synchronized (lock) {
81 setText(code);
82 }
83 }
84
85 public SourceIndex getSourceIndex() {
86 return this.sourceIndex;
87 }
88
89 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) {
90 decompileClass(classEntry, deobfuscator, null, callback);
91 }
92
93 public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) {
94
95 if (classEntry == null) {
96 setCode(null);
97 return;
98 }
99
100 setCode("(decompiling...)");
101
102 // run decompilation in a separate thread to keep ui responsive
103 new Thread() {
104 @Override
105 public void run() {
106
107 // decompile it
108 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName());
109 String source = deobfuscator.getSource(sourceTree);
110 setCode(source);
111 sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens);
112
113 if (callback != null) {
114 callback.run();
115 }
116 }
117 }.start();
118 }
119
120 public void navigateToClassDeclaration(ClassEntry classEntry) {
121
122 // navigate to the class declaration
123 Token token = this.sourceIndex.getDeclarationToken(classEntry);
124 if (token == null) {
125 // couldn't find the class declaration token, might be an anonymous class
126 // look for any declaration in that class instead
127 for (Entry entry : this.sourceIndex.declarations()) {
128 if (entry.getClassEntry().equals(classEntry)) {
129 token = this.sourceIndex.getDeclarationToken(entry);
130 break;
131 }
132 }
133 }
134
135 if (token != null) {
136 navigateToToken(token);
137 } else {
138 // couldn't find anything =(
139 System.out.println("Unable to find declaration in source for " + classEntry);
140 }
141 }
142
143 public void navigateToToken(final Token token) {
144 navigateToToken(this, token, this.selectionHighlightPainter);
145 }
146
147 // HACKHACK: someday we can update the main GUI to use this code reader
148 public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) {
149
150 // set the caret position to the token
151 editor.setCaretPosition(token.start);
152 editor.grabFocus();
153
154 try {
155 // make sure the token is visible in the scroll window
156 Rectangle start = editor.modelToView(token.start);
157 Rectangle end = editor.modelToView(token.end);
158 final Rectangle show = start.union(end);
159 show.grow(start.width * 10, start.height * 6);
160 SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show));
161 } catch (BadLocationException ex) {
162 throw new Error(ex);
163 }
164
165 // highlight the token momentarily
166 final Timer timer = new Timer(200, new ActionListener() {
167 private int m_counter = 0;
168 private Object m_highlight = null;
169
170 @Override
171 public void actionPerformed(ActionEvent event) {
172 if (m_counter % 2 == 0) {
173 try {
174 m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter);
175 } catch (BadLocationException ex) {
176 // don't care
177 }
178 } else if (m_highlight != null) {
179 editor.getHighlighter().removeHighlight(m_highlight);
180 }
181
182 if (m_counter++ > 6) {
183 Timer timer = (Timer) event.getSource();
184 timer.stop();
185 }
186 }
187 });
188 timer.start();
189 }
190
191 public void setHighlightedToken(Token token, HighlightPainter painter) {
192 try {
193 getHighlighter().addHighlight(token.start, token.end, painter);
194 } catch (BadLocationException ex) {
195 throw new IllegalArgumentException(ex);
196 }
197 }
198
199 public void clearHighlights() {
200 getHighlighter().removeAllHighlights();
201 }
202}
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 623e12ea..d93aa9fc 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -36,11 +36,17 @@ import cuchaz.enigma.gui.elements.MenuBar;
36import cuchaz.enigma.gui.elements.PopupMenuBar; 36import cuchaz.enigma.gui.elements.PopupMenuBar;
37import cuchaz.enigma.gui.filechooser.FileChooserFile; 37import cuchaz.enigma.gui.filechooser.FileChooserFile;
38import cuchaz.enigma.gui.filechooser.FileChooserFolder; 38import cuchaz.enigma.gui.filechooser.FileChooserFolder;
39import cuchaz.enigma.gui.highlight.DeobfuscatedHighlightPainter;
40import cuchaz.enigma.gui.highlight.ObfuscatedHighlightPainter;
41import cuchaz.enigma.gui.highlight.OtherHighlightPainter;
42import cuchaz.enigma.gui.highlight.SelectionHighlightPainter;
39import cuchaz.enigma.gui.panels.PanelDeobf; 43import cuchaz.enigma.gui.panels.PanelDeobf;
40import cuchaz.enigma.gui.panels.PanelEditor; 44import cuchaz.enigma.gui.panels.PanelEditor;
41import cuchaz.enigma.gui.panels.PanelIdentifier; 45import cuchaz.enigma.gui.panels.PanelIdentifier;
42import cuchaz.enigma.gui.panels.PanelObf; 46import cuchaz.enigma.gui.panels.PanelObf;
43import cuchaz.enigma.mapping.*; 47import cuchaz.enigma.mapping.*;
48import cuchaz.enigma.throwables.IllegalNameException;
49import cuchaz.enigma.utils.Utils;
44import de.sciss.syntaxpane.DefaultSyntaxKit; 50import de.sciss.syntaxpane.DefaultSyntaxKit;
45 51
46public class Gui { 52public class Gui {
@@ -363,7 +369,7 @@ public class Gui {
363 if (token == null) { 369 if (token == null) {
364 throw new IllegalArgumentException("Token cannot be null!"); 370 throw new IllegalArgumentException("Token cannot be null!");
365 } 371 }
366 CodeReader.navigateToToken(this.editor, token, m_selectionHighlightPainter); 372 Utils.navigateToToken(this.editor, token, m_selectionHighlightPainter);
367 redraw(); 373 redraw();
368 } 374 }
369 375
@@ -476,7 +482,7 @@ public class Gui {
476 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); 482 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
477 panel.add(label); 483 panel.add(label);
478 484
479 panel.add(GuiTricks.unboldLabel(new JLabel(value, JLabel.LEFT))); 485 panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT)));
480 } 486 }
481 487
482 public void onCaretMove(int pos) { 488 public void onCaretMove(int pos) {
@@ -498,13 +504,13 @@ public class Gui {
498 m_infoPanel.clearReference(); 504 m_infoPanel.clearReference();
499 } 505 }
500 506
501 this.popupMenu.renameMenu.setEnabled(isRenameable && isToken); 507 this.popupMenu.renameMenu.setEnabled(isRenameable);
502 this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); 508 this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry);
503 this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); 509 this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry);
504 this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); 510 this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry);
505 this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); 511 this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry));
506 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); 512 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation());
507 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable && isToken); 513 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable);
508 514
509 if (isToken && this.controller.entryHasDeobfuscatedName(m_reference.entry)) { 515 if (isToken && this.controller.entryHasDeobfuscatedName(m_reference.entry)) {
510 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); 516 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated");
@@ -526,7 +532,6 @@ public class Gui {
526 532
527 private void navigateTo(EntryReference<Entry, Entry> reference) { 533 private void navigateTo(EntryReference<Entry, Entry> reference) {
528 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { 534 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) {
529 // reference is not in the jar. Ignore it
530 return; 535 return;
531 } 536 }
532 if (m_reference != null) { 537 if (m_reference != null) {
@@ -574,7 +579,7 @@ public class Gui {
574 } catch (IllegalNameException ex) { 579 } catch (IllegalNameException ex) {
575 text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); 580 text.setBorder(BorderFactory.createLineBorder(Color.red, 1));
576 text.setToolTipText(ex.getReason()); 581 text.setToolTipText(ex.getReason());
577 GuiTricks.showToolTipNow(text); 582 Utils.showToolTipNow(text);
578 } 583 }
579 return; 584 return;
580 } 585 }
@@ -582,7 +587,7 @@ public class Gui {
582 // abort the rename 587 // abort the rename
583 JPanel panel = (JPanel) m_infoPanel.getComponent(0); 588 JPanel panel = (JPanel) m_infoPanel.getComponent(0);
584 panel.remove(panel.getComponentCount() - 1); 589 panel.remove(panel.getComponentCount() - 1);
585 panel.add(GuiTricks.unboldLabel(new JLabel(m_reference.getNamableName(), JLabel.LEFT))); 590 panel.add(Utils.unboldLabel(new JLabel(m_reference.getNamableName(), JLabel.LEFT)));
586 591
587 this.editor.grabFocus(); 592 this.editor.grabFocus();
588 593
@@ -704,6 +709,7 @@ public class Gui {
704 if (!this.controller.isDirty()) { 709 if (!this.controller.isDirty()) {
705 // everything is saved, we can exit safely 710 // everything is saved, we can exit safely
706 this.frame.dispose(); 711 this.frame.dispose();
712 System.exit(0);
707 } else { 713 } else {
708 // ask to save before closing 714 // ask to save before closing
709 String[] options = {"Save and exit", "Discard changes", "Cancel"}; 715 String[] options = {"Save and exit", "Discard changes", "Cancel"};
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index 37244ff5..c3015948 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -27,6 +27,8 @@ import cuchaz.enigma.Deobfuscator;
27import cuchaz.enigma.analysis.*; 27import cuchaz.enigma.analysis.*;
28import cuchaz.enigma.gui.dialog.ProgressDialog; 28import cuchaz.enigma.gui.dialog.ProgressDialog;
29import cuchaz.enigma.mapping.*; 29import cuchaz.enigma.mapping.*;
30import cuchaz.enigma.throwables.MappingParseException;
31import cuchaz.enigma.utils.ReadableToken;
30 32
31public class GuiController { 33public class GuiController {
32 34
@@ -72,7 +74,7 @@ public class GuiController {
72 refreshCurrentClass(); 74 refreshCurrentClass();
73 } 75 }
74 76
75 public void openMappings(File file) throws IOException, MappingParseException { 77 public void openMappings(File file) throws IOException {
76 this.deobfuscator.setMappings(new MappingsReader().read(file)); 78 this.deobfuscator.setMappings(new MappingsReader().read(file));
77 this.isDirty = false; 79 this.isDirty = false;
78 this.gui.setMappingsFile(file); 80 this.gui.setMappingsFile(file);
diff --git a/src/main/java/cuchaz/enigma/gui/GuiTricks.java b/src/main/java/cuchaz/enigma/gui/GuiTricks.java
deleted file mode 100644
index ffacfecf..00000000
--- a/src/main/java/cuchaz/enigma/gui/GuiTricks.java
+++ /dev/null
@@ -1,52 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.awt.Font;
14import java.awt.event.ActionListener;
15import java.awt.event.MouseEvent;
16import java.util.Arrays;
17
18import javax.swing.JButton;
19import javax.swing.JComponent;
20import javax.swing.JLabel;
21import javax.swing.ToolTipManager;
22
23public class GuiTricks {
24
25 public static JLabel unboldLabel(JLabel label) {
26 Font font = label.getFont();
27 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD));
28 return label;
29 }
30
31 public static void showToolTipNow(JComponent component) {
32 // HACKHACK: trick the tooltip manager into showing the tooltip right now
33 ToolTipManager manager = ToolTipManager.sharedInstance();
34 int oldDelay = manager.getInitialDelay();
35 manager.setInitialDelay(0);
36 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false));
37 manager.setInitialDelay(oldDelay);
38 }
39
40 public static void deactivateButton(JButton button) {
41 button.setEnabled(false);
42 button.setText("");
43 Arrays.asList(button.getActionListeners()).forEach(button::removeActionListener);
44 }
45
46 public static void activateButton(JButton button, String text, ActionListener newListener) {
47 button.setText(text);
48 button.setEnabled(true);
49 Arrays.asList(button.getActionListeners()).forEach(button::removeActionListener);
50 button.addActionListener(newListener);
51 }
52}
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 0713ad58..00000000
--- a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
+++ /dev/null
@@ -1,442 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import com.google.common.collect.Lists;
14import com.google.common.collect.Maps;
15
16import java.awt.BorderLayout;
17import java.awt.Container;
18import java.awt.Dimension;
19import java.awt.FlowLayout;
20import java.awt.event.ActionListener;
21import java.awt.event.KeyAdapter;
22import java.awt.event.KeyEvent;
23import java.util.Collection;
24import java.util.List;
25import java.util.Map;
26
27import javax.swing.*;
28import javax.swing.text.Highlighter.HighlightPainter;
29
30import cuchaz.enigma.Constants;
31import cuchaz.enigma.Deobfuscator;
32import cuchaz.enigma.analysis.SourceIndex;
33import cuchaz.enigma.analysis.Token;
34import cuchaz.enigma.convert.ClassMatches;
35import cuchaz.enigma.convert.MemberMatches;
36import cuchaz.enigma.mapping.ClassEntry;
37import cuchaz.enigma.mapping.Entry;
38import de.sciss.syntaxpane.DefaultSyntaxKit;
39
40
41public class MemberMatchingGui<T extends Entry> {
42
43 private enum SourceType {
44 Matched {
45 @Override
46 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) {
47 return matches.getSourceClassesWithoutUnmatchedEntries();
48 }
49 },
50 Unmatched {
51 @Override
52 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) {
53 return matches.getSourceClassesWithUnmatchedEntries();
54 }
55 };
56
57 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
58 JRadioButton button = new JRadioButton(name(), this == getDefault());
59 button.setActionCommand(name());
60 button.addActionListener(listener);
61 group.add(button);
62 return button;
63 }
64
65 public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches);
66
67 public static SourceType getDefault() {
68 return values()[0];
69 }
70 }
71
72 public interface SaveListener<T extends Entry> {
73 void save(MemberMatches<T> matches);
74 }
75
76 // controls
77 private JFrame m_frame;
78 private Map<SourceType, JRadioButton> m_sourceTypeButtons;
79 private ClassSelector m_sourceClasses;
80 private CodeReader m_sourceReader;
81 private CodeReader m_destReader;
82 private JButton m_matchButton;
83 private JButton m_unmatchableButton;
84 private JLabel m_sourceLabel;
85 private JLabel m_destLabel;
86 private HighlightPainter m_unmatchedHighlightPainter;
87 private HighlightPainter m_matchedHighlightPainter;
88
89 private ClassMatches m_classMatches;
90 private MemberMatches<T> m_memberMatches;
91 private Deobfuscator m_sourceDeobfuscator;
92 private Deobfuscator m_destDeobfuscator;
93 private SaveListener<T> m_saveListener;
94 private SourceType m_sourceType;
95 private ClassEntry m_obfSourceClass;
96 private ClassEntry m_obfDestClass;
97 private T m_obfSourceEntry;
98 private T m_obfDestEntry;
99
100 public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
101
102 m_classMatches = classMatches;
103 m_memberMatches = fieldMatches;
104 m_sourceDeobfuscator = sourceDeobfuscator;
105 m_destDeobfuscator = destDeobfuscator;
106
107 // init frame
108 m_frame = new JFrame(Constants.NAME + " - Member Matcher");
109 final Container pane = m_frame.getContentPane();
110 pane.setLayout(new BorderLayout());
111
112 // init classes side
113 JPanel classesPanel = new JPanel();
114 classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS));
115 classesPanel.setPreferredSize(new Dimension(200, 0));
116 pane.add(classesPanel, BorderLayout.WEST);
117 classesPanel.add(new JLabel("Classes"));
118
119 // init source type radios
120 JPanel sourceTypePanel = new JPanel();
121 classesPanel.add(sourceTypePanel);
122 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
123 ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand()));
124 ButtonGroup sourceTypeButtons = new ButtonGroup();
125 m_sourceTypeButtons = Maps.newHashMap();
126 for (SourceType sourceType : SourceType.values()) {
127 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
128 m_sourceTypeButtons.put(sourceType, button);
129 sourceTypePanel.add(button);
130 }
131
132 m_sourceClasses = new ClassSelector(ClassSelector.DEOBF_CLASS_COMPARATOR);
133 m_sourceClasses.setListener(this::setSourceClass);
134 JScrollPane sourceScroller = new JScrollPane(m_sourceClasses);
135 classesPanel.add(sourceScroller);
136
137 // init readers
138 DefaultSyntaxKit.initKit();
139 m_sourceReader = new CodeReader();
140 m_sourceReader.setSelectionListener(reference -> {
141 if (reference != null) {
142 onSelectSource(reference.entry);
143 } else {
144 onSelectSource(null);
145 }
146 });
147 m_destReader = new CodeReader();
148 m_destReader.setSelectionListener(reference -> {
149 if (reference != null) {
150 onSelectDest(reference.entry);
151 } else {
152 onSelectDest(null);
153 }
154 });
155
156 // add key bindings
157 KeyAdapter keyListener = new KeyAdapter() {
158 @Override
159 public void keyPressed(KeyEvent event) {
160 switch (event.getKeyCode()) {
161 case KeyEvent.VK_M:
162 m_matchButton.doClick();
163 break;
164 }
165 }
166 };
167 m_sourceReader.addKeyListener(keyListener);
168 m_destReader.addKeyListener(keyListener);
169
170 // init all the splits
171 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader));
172 splitRight.setResizeWeight(0.5); // resize 50:50
173 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight);
174 splitLeft.setResizeWeight(0); // let the right side take all the slack
175 pane.add(splitLeft, BorderLayout.CENTER);
176 splitLeft.resetToPreferredSizes();
177
178 // init bottom panel
179 JPanel bottomPanel = new JPanel();
180 bottomPanel.setLayout(new FlowLayout());
181 pane.add(bottomPanel, BorderLayout.SOUTH);
182
183 m_matchButton = new JButton();
184 m_unmatchableButton = new JButton();
185
186 m_sourceLabel = new JLabel();
187 bottomPanel.add(m_sourceLabel);
188 bottomPanel.add(m_matchButton);
189 bottomPanel.add(m_unmatchableButton);
190 m_destLabel = new JLabel();
191 bottomPanel.add(m_destLabel);
192
193 // show the frame
194 pane.doLayout();
195 m_frame.setSize(1024, 576);
196 m_frame.setMinimumSize(new Dimension(640, 480));
197 m_frame.setVisible(true);
198 m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
199
200 m_unmatchedHighlightPainter = new ObfuscatedHighlightPainter();
201 m_matchedHighlightPainter = new DeobfuscatedHighlightPainter();
202
203 // init state
204 m_saveListener = null;
205 m_obfSourceClass = null;
206 m_obfDestClass = null;
207 m_obfSourceEntry = null;
208 m_obfDestEntry = null;
209 setSourceType(SourceType.getDefault());
210 updateButtons();
211 }
212
213 protected void setSourceType(SourceType val) {
214 m_sourceType = val;
215 updateSourceClasses();
216 }
217
218 public void setSaveListener(SaveListener<T> val) {
219 m_saveListener = val;
220 }
221
222 private void updateSourceClasses() {
223
224 String selectedPackage = m_sourceClasses.getSelectedPackage();
225
226 List<ClassEntry> deobfClassEntries = Lists.newArrayList();
227 for (ClassEntry entry : m_sourceType.getObfSourceClasses(m_memberMatches)) {
228 deobfClassEntries.add(m_sourceDeobfuscator.deobfuscateEntry(entry));
229 }
230 m_sourceClasses.setClasses(deobfClassEntries);
231
232 if (selectedPackage != null) {
233 m_sourceClasses.expandPackage(selectedPackage);
234 }
235
236 for (SourceType sourceType : SourceType.values()) {
237 m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
238 sourceType.name(), sourceType.getObfSourceClasses(m_memberMatches).size()
239 ));
240 }
241 }
242
243 protected void setSourceClass(ClassEntry sourceClass) {
244
245 m_obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass);
246 m_obfDestClass = m_classMatches.getUniqueMatches().get(m_obfSourceClass);
247 if (m_obfDestClass == null) {
248 throw new Error("No matching dest class for source class: " + m_obfSourceClass);
249 }
250
251 m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, false, this::updateSourceHighlights);
252 m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, false, this::updateDestHighlights);
253 }
254
255 protected void updateSourceHighlights() {
256 highlightEntries(m_sourceReader, m_sourceDeobfuscator, m_memberMatches.matches().keySet(), m_memberMatches.getUnmatchedSourceEntries());
257 }
258
259 protected void updateDestHighlights() {
260 highlightEntries(m_destReader, m_destDeobfuscator, m_memberMatches.matches().values(), m_memberMatches.getUnmatchedDestEntries());
261 }
262
263 private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) {
264 reader.clearHighlights();
265 SourceIndex index = reader.getSourceIndex();
266
267 // matched fields
268 for (T obfT : obfMatchedEntries) {
269 T deobfT = deobfuscator.deobfuscateEntry(obfT);
270 Token token = index.getDeclarationToken(deobfT);
271 if (token != null) {
272 reader.setHighlightedToken(token, m_matchedHighlightPainter);
273 }
274 }
275
276 // unmatched fields
277 for (T obfT : obfUnmatchedEntries) {
278 T deobfT = deobfuscator.deobfuscateEntry(obfT);
279 Token token = index.getDeclarationToken(deobfT);
280 if (token != null) {
281 reader.setHighlightedToken(token, m_unmatchedHighlightPainter);
282 }
283 }
284 }
285
286 private boolean isSelectionMatched() {
287 return m_obfSourceEntry != null && m_obfDestEntry != null
288 && m_memberMatches.isMatched(m_obfSourceEntry, m_obfDestEntry);
289 }
290
291 protected void onSelectSource(Entry source) {
292
293 // start with no selection
294 if (isSelectionMatched()) {
295 setDest(null);
296 }
297 setSource(null);
298
299 // then look for a valid source selection
300 if (source != null) {
301
302 // this looks really scary, but it's actually ok
303 // Deobfuscator.obfuscateEntry can handle all implementations of Entry
304 // and MemberMatches.hasSource() will only pass entries that actually match T
305 @SuppressWarnings("unchecked")
306 T sourceEntry = (T) source;
307
308 T obfSourceEntry = m_sourceDeobfuscator.obfuscateEntry(sourceEntry);
309 if (m_memberMatches.hasSource(obfSourceEntry)) {
310 setSource(obfSourceEntry);
311
312 // look for a matched dest too
313 T obfDestEntry = m_memberMatches.matches().get(obfSourceEntry);
314 if (obfDestEntry != null) {
315 setDest(obfDestEntry);
316 }
317 }
318 }
319
320 updateButtons();
321 }
322
323 protected void onSelectDest(Entry dest) {
324
325 // start with no selection
326 if (isSelectionMatched()) {
327 setSource(null);
328 }
329 setDest(null);
330
331 // then look for a valid dest selection
332 if (dest != null) {
333
334 // this looks really scary, but it's actually ok
335 // Deobfuscator.obfuscateEntry can handle all implementations of Entry
336 // and MemberMatches.hasSource() will only pass entries that actually match T
337 @SuppressWarnings("unchecked")
338 T destEntry = (T) dest;
339
340 T obfDestEntry = m_destDeobfuscator.obfuscateEntry(destEntry);
341 if (m_memberMatches.hasDest(obfDestEntry)) {
342 setDest(obfDestEntry);
343
344 // look for a matched source too
345 T obfSourceEntry = m_memberMatches.matches().inverse().get(obfDestEntry);
346 if (obfSourceEntry != null) {
347 setSource(obfSourceEntry);
348 }
349 }
350 }
351
352 updateButtons();
353 }
354
355 private void setSource(T obfEntry) {
356 m_obfSourceEntry = obfEntry;
357 if (obfEntry == null) {
358 m_sourceLabel.setText("");
359 } else {
360 m_sourceLabel.setText(getEntryLabel(obfEntry, m_sourceDeobfuscator));
361 }
362 }
363
364 private void setDest(T obfEntry) {
365 m_obfDestEntry = obfEntry;
366 if (obfEntry == null) {
367 m_destLabel.setText("");
368 } else {
369 m_destLabel.setText(getEntryLabel(obfEntry, m_destDeobfuscator));
370 }
371 }
372
373 private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) {
374 // show obfuscated and deobfuscated names, but no types/signatures
375 T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry);
376 return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName());
377 }
378
379 private void updateButtons() {
380
381 GuiTricks.deactivateButton(m_matchButton);
382 GuiTricks.deactivateButton(m_unmatchableButton);
383
384 if (m_obfSourceEntry != null && m_obfDestEntry != null) {
385 if (m_memberMatches.isMatched(m_obfSourceEntry, m_obfDestEntry)) {
386 GuiTricks.activateButton(m_matchButton, "Unmatch", event -> unmatch());
387 } else if (!m_memberMatches.isMatchedSourceEntry(m_obfSourceEntry) && !m_memberMatches.isMatchedDestEntry(m_obfDestEntry)) {
388 GuiTricks.activateButton(m_matchButton, "Match", event -> match());
389 }
390 } else if (m_obfSourceEntry != null) {
391 GuiTricks.activateButton(m_unmatchableButton, "Set Unmatchable", event -> unmatchable());
392 }
393 }
394
395 protected void match() {
396
397 // update the field matches
398 m_memberMatches.makeMatch(m_obfSourceEntry, m_obfDestEntry);
399 save();
400
401 // update the ui
402 onSelectSource(null);
403 onSelectDest(null);
404 updateSourceHighlights();
405 updateDestHighlights();
406 updateSourceClasses();
407 }
408
409 protected void unmatch() {
410
411 // update the field matches
412 m_memberMatches.unmakeMatch(m_obfSourceEntry, m_obfDestEntry);
413 save();
414
415 // update the ui
416 onSelectSource(null);
417 onSelectDest(null);
418 updateSourceHighlights();
419 updateDestHighlights();
420 updateSourceClasses();
421 }
422
423 protected void unmatchable() {
424
425 // update the field matches
426 m_memberMatches.makeSourceUnmatchable(m_obfSourceEntry);
427 save();
428
429 // update the ui
430 onSelectSource(null);
431 onSelectDest(null);
432 updateSourceHighlights();
433 updateDestHighlights();
434 updateSourceClasses();
435 }
436
437 private void save() {
438 if (m_saveListener != null) {
439 m_saveListener.save(m_memberMatches);
440 }
441 }
442}
diff --git a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
deleted file mode 100644
index 359ec7ae..00000000
--- a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
+++ /dev/null
@@ -1,29 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import cuchaz.enigma.mapping.ClassEntry;
14
15public class ScoredClassEntry extends ClassEntry {
16
17 private static final long serialVersionUID = -8798725308554217105L;
18
19 private float score;
20
21 public ScoredClassEntry(ClassEntry other, float score) {
22 super(other);
23 this.score = score;
24 }
25
26 public float getScore() {
27 return this.score;
28 }
29}
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
index da4f5fb5..f690b159 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
@@ -19,7 +19,7 @@ import java.io.IOException;
19import javax.swing.*; 19import javax.swing.*;
20 20
21import cuchaz.enigma.Constants; 21import cuchaz.enigma.Constants;
22import cuchaz.enigma.Util; 22import cuchaz.enigma.utils.Utils;
23 23
24public class AboutDialog { 24public class AboutDialog {
25 25
@@ -31,7 +31,7 @@ public class AboutDialog {
31 31
32 // load the content 32 // load the content
33 try { 33 try {
34 String html = Util.readResourceToString("/about.html"); 34 String html = Utils.readResourceToString("/about.html");
35 html = String.format(html, Constants.NAME, Constants.VERSION); 35 html = String.format(html, Constants.NAME, Constants.VERSION);
36 JLabel label = new JLabel(html); 36 JLabel label = new JLabel(html);
37 label.setHorizontalAlignment(JLabel.CENTER); 37 label.setHorizontalAlignment(JLabel.CENTER);
@@ -44,7 +44,7 @@ public class AboutDialog {
44 String html = "<html><a href=\"%s\">%s</a></html>"; 44 String html = "<html><a href=\"%s\">%s</a></html>";
45 html = String.format(html, Constants.URL, Constants.URL); 45 html = String.format(html, Constants.URL, Constants.URL);
46 JButton link = new JButton(html); 46 JButton link = new JButton(html);
47 link.addActionListener(event -> Util.openUrl(Constants.URL)); 47 link.addActionListener(event -> Utils.openUrl(Constants.URL));
48 link.setBorderPainted(false); 48 link.setBorderPainted(false);
49 link.setOpaque(false); 49 link.setOpaque(false);
50 link.setBackground(Color.WHITE); 50 link.setBackground(Color.WHITE);
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
index 71aab015..8d3df47b 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
@@ -19,7 +19,7 @@ import java.io.StringWriter;
19import javax.swing.*; 19import javax.swing.*;
20 20
21import cuchaz.enigma.Constants; 21import cuchaz.enigma.Constants;
22import cuchaz.enigma.gui.GuiTricks; 22import cuchaz.enigma.utils.Utils;
23 23
24public class CrashDialog { 24public class CrashDialog {
25 25
@@ -48,7 +48,7 @@ public class CrashDialog {
48 FlowLayout buttonsLayout = new FlowLayout(); 48 FlowLayout buttonsLayout = new FlowLayout();
49 buttonsLayout.setAlignment(FlowLayout.RIGHT); 49 buttonsLayout.setAlignment(FlowLayout.RIGHT);
50 buttonsPanel.setLayout(buttonsLayout); 50 buttonsPanel.setLayout(buttonsLayout);
51 buttonsPanel.add(GuiTricks.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); 51 buttonsPanel.add(Utils.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work.")));
52 JButton ignoreButton = new JButton("Ignore"); 52 JButton ignoreButton = new JButton("Ignore");
53 ignoreButton.addActionListener(event -> { 53 ignoreButton.addActionListener(event -> {
54 // close (hide) the dialog 54 // close (hide) the dialog
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
index dc4d91e8..c752c868 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
@@ -19,7 +19,7 @@ import javax.swing.*;
19 19
20import cuchaz.enigma.Constants; 20import cuchaz.enigma.Constants;
21import cuchaz.enigma.Deobfuscator.ProgressListener; 21import cuchaz.enigma.Deobfuscator.ProgressListener;
22import cuchaz.enigma.gui.GuiTricks; 22import cuchaz.enigma.utils.Utils;
23 23
24public class ProgressDialog implements ProgressListener, AutoCloseable { 24public class ProgressDialog implements ProgressListener, AutoCloseable {
25 25
@@ -44,7 +44,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable {
44 JPanel panel = new JPanel(); 44 JPanel panel = new JPanel();
45 pane.add(panel); 45 pane.add(panel);
46 panel.setLayout(new BorderLayout()); 46 panel.setLayout(new BorderLayout());
47 this.labelText = GuiTricks.unboldLabel(new JLabel()); 47 this.labelText = Utils.unboldLabel(new JLabel());
48 this.progress = new JProgressBar(); 48 this.progress = new JProgressBar();
49 this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); 49 this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
50 panel.add(this.labelText, BorderLayout.NORTH); 50 panel.add(this.labelText, BorderLayout.NORTH);
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
index 233d55e2..e8703340 100644
--- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
+++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
@@ -9,7 +9,7 @@ import javax.swing.*;
9 9
10import cuchaz.enigma.gui.Gui; 10import cuchaz.enigma.gui.Gui;
11import cuchaz.enigma.gui.dialog.AboutDialog; 11import cuchaz.enigma.gui.dialog.AboutDialog;
12import cuchaz.enigma.mapping.MappingParseException; 12import cuchaz.enigma.throwables.MappingParseException;
13 13
14public class MenuBar extends JMenuBar { 14public class MenuBar extends JMenuBar {
15 15
@@ -69,8 +69,6 @@ public class MenuBar extends JMenuBar {
69 this.gui.getController().openMappings(this.gui.mappingsFileChooser.getSelectedFile()); 69 this.gui.getController().openMappings(this.gui.mappingsFileChooser.getSelectedFile());
70 } catch (IOException ex) { 70 } catch (IOException ex) {
71 throw new Error(ex); 71 throw new Error(ex);
72 } catch (MappingParseException ex) {
73 JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage());
74 } 72 }
75 } 73 }
76 }); 74 });
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
index bd8f0f0b..93ca5d68 100644
--- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
+++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
@@ -2,7 +2,7 @@ package cuchaz.enigma.gui.filechooser;
2 2
3import javax.swing.JFileChooser; 3import javax.swing.JFileChooser;
4 4
5public class FileChooserFolder extends JFileChooser{ 5public class FileChooserFolder extends JFileChooser {
6 6
7 public FileChooserFolder() { 7 public FileChooserFolder() {
8 this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 8 this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
diff --git a/src/main/java/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
index b66d13da..0a730880 100644
--- a/src/main/java/cuchaz/enigma/gui/BoxHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.Color; 13import java.awt.Color;
14import java.awt.Graphics; 14import java.awt.Graphics;
diff --git a/src/main/java/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java
index d5ad0c8a..5d572030 100644
--- a/src/main/java/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.Color; 13import java.awt.Color;
14 14
diff --git a/src/main/java/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java
index 5afc767f..ee64d86a 100644
--- a/src/main/java/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.Color; 13import java.awt.Color;
14 14
diff --git a/src/main/java/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java
index 256f69eb..43d8352e 100644
--- a/src/main/java/cuchaz/enigma/gui/OtherHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.Color; 13import java.awt.Color;
14 14
diff --git a/src/main/java/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
index fcad07cd..f772284f 100644
--- a/src/main/java/cuchaz/enigma/gui/SelectionHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.*; 13import java.awt.*;
14 14
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
index e73340a6..e0835725 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelectorClassNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.node;
12 12
13import javax.swing.tree.DefaultMutableTreeNode; 13import javax.swing.tree.DefaultMutableTreeNode;
14 14
@@ -16,8 +16,6 @@ import cuchaz.enigma.mapping.ClassEntry;
16 16
17public class ClassSelectorClassNode extends DefaultMutableTreeNode { 17public class ClassSelectorClassNode extends DefaultMutableTreeNode {
18 18
19 private static final long serialVersionUID = -8956754339813257380L;
20
21 private ClassEntry classEntry; 19 private ClassEntry classEntry;
22 20
23 public ClassSelectorClassNode(ClassEntry classEntry) { 21 public ClassSelectorClassNode(ClassEntry classEntry) {
@@ -30,9 +28,6 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode {
30 28
31 @Override 29 @Override
32 public String toString() { 30 public String toString() {
33 if (this.classEntry instanceof ScoredClassEntry) {
34 return String.format("%d%% %s", (int) ((ScoredClassEntry) this.classEntry).getScore(), this.classEntry.getSimpleName());
35 }
36 return this.classEntry.getSimpleName(); 31 return this.classEntry.getSimpleName();
37 } 32 }
38 33
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
index 3b5ba8ca..805b3a8e 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelectorPackageNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
@@ -8,24 +8,18 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.node;
12 12
13import javax.swing.tree.DefaultMutableTreeNode; 13import javax.swing.tree.DefaultMutableTreeNode;
14 14
15public class ClassSelectorPackageNode extends DefaultMutableTreeNode { 15public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
16 16
17 private static final long serialVersionUID = -3730868701219548043L;
18
19 private String packageName; 17 private String packageName;
20 18
21 public ClassSelectorPackageNode(String packageName) { 19 public ClassSelectorPackageNode(String packageName) {
22 this.packageName = packageName; 20 this.packageName = packageName;
23 } 21 }
24 22
25 public String getPackageName() {
26 return this.packageName;
27 }
28
29 @Override 23 @Override
30 public String toString() { 24 public String toString() {
31 return this.packageName; 25 return this.packageName;
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
index 2cc8b76a..bba71327 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
@@ -23,6 +23,5 @@ public class PanelDeobf extends JPanel {
23 this.setLayout(new BorderLayout()); 23 this.setLayout(new BorderLayout());
24 this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); 24 this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH);
25 this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); 25 this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER);
26
27 } 26 }
28} 27}
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
index 4261eb5a..faa023bd 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
@@ -8,7 +8,7 @@ import javax.swing.JLabel;
8import javax.swing.JPanel; 8import javax.swing.JPanel;
9 9
10import cuchaz.enigma.gui.Gui; 10import cuchaz.enigma.gui.Gui;
11import cuchaz.enigma.gui.GuiTricks; 11import cuchaz.enigma.utils.Utils;
12 12
13public class PanelIdentifier extends JPanel { 13public class PanelIdentifier extends JPanel {
14 14
@@ -25,7 +25,7 @@ public class PanelIdentifier extends JPanel {
25 public void clearReference() { 25 public void clearReference() {
26 this.removeAll(); 26 this.removeAll();
27 JLabel label = new JLabel("No identifier selected"); 27 JLabel label = new JLabel("No identifier selected");
28 GuiTricks.unboldLabel(label); 28 Utils.unboldLabel(label);
29 label.setHorizontalAlignment(JLabel.CENTER); 29 label.setHorizontalAlignment(JLabel.CENTER);
30 this.add(label); 30 this.add(label);
31 31
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
index 3e0374e7..94b384fd 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
@@ -19,7 +19,7 @@ public class PanelObf extends JPanel {
19 public PanelObf(Gui gui) { 19 public PanelObf(Gui gui) {
20 this.gui = gui; 20 this.gui = gui;
21 21
22 Comparator<ClassEntry> obfClassComparator = (a, b) -> { 22 Comparator<ClassEntry> obfClassComparator = (a, b) -> {
23 String aname = a.getName(); 23 String aname = a.getName();
24 String bname = b.getName(); 24 String bname = b.getName();
25 if (aname.length() != bname.length()) { 25 if (aname.length() != bname.length()) {
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
index 83033bc5..1409fc43 100644
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
@@ -10,17 +10,13 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.io.Serializable; 13import cuchaz.enigma.utils.Utils;
14 14
15import cuchaz.enigma.Util; 15public class ArgumentEntry implements Entry {
16 16
17public class ArgumentEntry implements Entry, Serializable { 17 private BehaviorEntry behaviorEntry;
18 18 private int index;
19 private static final long serialVersionUID = 4472172468162696006L; 19 private String name;
20
21 private BehaviorEntry m_behaviorEntry;
22 private int m_index;
23 private String m_name;
24 20
25 public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { 21 public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) {
26 if (behaviorEntry == null) { 22 if (behaviorEntry == null) {
@@ -33,44 +29,38 @@ public class ArgumentEntry implements Entry, Serializable {
33 throw new IllegalArgumentException("Argument name cannot be null!"); 29 throw new IllegalArgumentException("Argument name cannot be null!");
34 } 30 }
35 31
36 m_behaviorEntry = behaviorEntry; 32 this.behaviorEntry = behaviorEntry;
37 m_index = index; 33 this.index = index;
38 m_name = name; 34 this.name = name;
39 }
40
41 public ArgumentEntry(ArgumentEntry other) {
42 m_behaviorEntry = (BehaviorEntry) m_behaviorEntry.cloneToNewClass(getClassEntry());
43 m_index = other.m_index;
44 m_name = other.m_name;
45 } 35 }
46 36
47 public ArgumentEntry(ArgumentEntry other, String newClassName) { 37 public ArgumentEntry(ArgumentEntry other, String newClassName) {
48 m_behaviorEntry = (BehaviorEntry) other.m_behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); 38 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName));
49 m_index = other.m_index; 39 this.index = other.index;
50 m_name = other.m_name; 40 this.name = other.name;
51 } 41 }
52 42
53 public BehaviorEntry getBehaviorEntry() { 43 public BehaviorEntry getBehaviorEntry() {
54 return m_behaviorEntry; 44 return this.behaviorEntry;
55 } 45 }
56 46
57 public int getIndex() { 47 public int getIndex() {
58 return m_index; 48 return this.index;
59 } 49 }
60 50
61 @Override 51 @Override
62 public String getName() { 52 public String getName() {
63 return m_name; 53 return this.name;
64 } 54 }
65 55
66 @Override 56 @Override
67 public ClassEntry getClassEntry() { 57 public ClassEntry getClassEntry() {
68 return m_behaviorEntry.getClassEntry(); 58 return this.behaviorEntry.getClassEntry();
69 } 59 }
70 60
71 @Override 61 @Override
72 public String getClassName() { 62 public String getClassName() {
73 return m_behaviorEntry.getClassName(); 63 return this.behaviorEntry.getClassName();
74 } 64 }
75 65
76 @Override 66 @Override
@@ -79,20 +69,16 @@ public class ArgumentEntry implements Entry, Serializable {
79 } 69 }
80 70
81 public String getMethodName() { 71 public String getMethodName() {
82 return m_behaviorEntry.getName(); 72 return this.behaviorEntry.getName();
83 } 73 }
84 74
85 public Signature getMethodSignature() { 75 public Signature getMethodSignature() {
86 return m_behaviorEntry.getSignature(); 76 return this.behaviorEntry.getSignature();
87 } 77 }
88 78
89 @Override 79 @Override
90 public int hashCode() { 80 public int hashCode() {
91 return Util.combineHashesOrdered( 81 return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode());
92 m_behaviorEntry,
93 Integer.valueOf(m_index).hashCode(),
94 m_name.hashCode()
95 );
96 } 82 }
97 83
98 @Override 84 @Override
@@ -101,11 +87,11 @@ public class ArgumentEntry implements Entry, Serializable {
101 } 87 }
102 88
103 public boolean equals(ArgumentEntry other) { 89 public boolean equals(ArgumentEntry other) {
104 return m_behaviorEntry.equals(other.m_behaviorEntry) && m_index == other.m_index && m_name.equals(other.m_name); 90 return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name);
105 } 91 }
106 92
107 @Override 93 @Override
108 public String toString() { 94 public String toString() {
109 return m_behaviorEntry.toString() + "(" + m_index + ":" + m_name + ")"; 95 return this.behaviorEntry.toString() + "(" + this.index + ":" + this.name + ")";
110 } 96 }
111} 97}
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
index 2b77d6ea..918395f9 100644
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
@@ -10,40 +10,31 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.io.Serializable; 13public class ArgumentMapping implements Comparable<ArgumentMapping> {
14 14
15public class ArgumentMapping implements Serializable, Comparable<ArgumentMapping> { 15 private int index;
16 16 private String name;
17 private static final long serialVersionUID = 8610742471440861315L;
18
19 private int m_index;
20 private String m_name;
21 17
22 // NOTE: this argument order is important for the MethodReader/MethodWriter 18 // NOTE: this argument order is important for the MethodReader/MethodWriter
23 public ArgumentMapping(int index, String name) { 19 public ArgumentMapping(int index, String name) {
24 m_index = index; 20 this.index = index;
25 m_name = NameValidator.validateArgumentName(name); 21 this.name = NameValidator.validateArgumentName(name);
26 }
27
28 public ArgumentMapping(ArgumentMapping other) {
29 m_index = other.m_index;
30 m_name = other.m_name;
31 } 22 }
32 23
33 public int getIndex() { 24 public int getIndex() {
34 return m_index; 25 return this.index;
35 } 26 }
36 27
37 public String getName() { 28 public String getName() {
38 return m_name; 29 return this.name;
39 } 30 }
40 31
41 public void setName(String val) { 32 public void setName(String val) {
42 m_name = NameValidator.validateArgumentName(val); 33 this.name = NameValidator.validateArgumentName(val);
43 } 34 }
44 35
45 @Override 36 @Override
46 public int compareTo(ArgumentMapping other) { 37 public int compareTo(ArgumentMapping other) {
47 return Integer.compare(m_index, other.m_index); 38 return Integer.compare(this.index, other.index);
48 } 39 }
49} 40}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
index c9304d45..a58d0548 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
@@ -12,14 +12,11 @@ package cuchaz.enigma.mapping;
12 12
13import com.google.common.collect.Lists; 13import com.google.common.collect.Lists;
14 14
15import java.io.Serializable;
16import java.util.List; 15import java.util.List;
17 16
18public class ClassEntry implements Entry, Serializable { 17public class ClassEntry implements Entry {
19 18
20 private static final long serialVersionUID = 4235460580973955811L; 19 private String name;
21
22 private String m_name;
23 20
24 public ClassEntry(String className) { 21 public ClassEntry(String className) {
25 if (className == null) { 22 if (className == null) {
@@ -29,7 +26,7 @@ public class ClassEntry implements Entry, Serializable {
29 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); 26 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
30 } 27 }
31 28
32 m_name = className; 29 this.name = className;
33 30
34 if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { 31 if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) {
35 throw new IllegalArgumentException("Inner class must not have a package: " + className); 32 throw new IllegalArgumentException("Inner class must not have a package: " + className);
@@ -37,17 +34,17 @@ public class ClassEntry implements Entry, Serializable {
37 } 34 }
38 35
39 public ClassEntry(ClassEntry other) { 36 public ClassEntry(ClassEntry other) {
40 m_name = other.m_name; 37 this.name = other.name;
41 } 38 }
42 39
43 @Override 40 @Override
44 public String getName() { 41 public String getName() {
45 return m_name; 42 return this.name;
46 } 43 }
47 44
48 @Override 45 @Override
49 public String getClassName() { 46 public String getClassName() {
50 return m_name; 47 return this.name;
51 } 48 }
52 49
53 @Override 50 @Override
@@ -62,7 +59,7 @@ public class ClassEntry implements Entry, Serializable {
62 59
63 @Override 60 @Override
64 public int hashCode() { 61 public int hashCode() {
65 return m_name.hashCode(); 62 return this.name.hashCode();
66 } 63 }
67 64
68 @Override 65 @Override
@@ -71,20 +68,20 @@ public class ClassEntry implements Entry, Serializable {
71 } 68 }
72 69
73 public boolean equals(ClassEntry other) { 70 public boolean equals(ClassEntry other) {
74 return m_name.equals(other.m_name); 71 return this.name.equals(other.name);
75 } 72 }
76 73
77 @Override 74 @Override
78 public String toString() { 75 public String toString() {
79 return m_name; 76 return this.name;
80 } 77 }
81 78
82 public boolean isInnerClass() { 79 public boolean isInnerClass() {
83 return m_name.lastIndexOf('$') >= 0; 80 return this.name.lastIndexOf('$') >= 0;
84 } 81 }
85 82
86 public List<String> getClassChainNames() { 83 public List<String> getClassChainNames() {
87 return Lists.newArrayList(m_name.split("\\$")); 84 return Lists.newArrayList(this.name.split("\\$"));
88 } 85 }
89 86
90 public List<ClassEntry> getClassChain() { 87 public List<ClassEntry> getClassChain() {
@@ -102,9 +99,9 @@ public class ClassEntry implements Entry, Serializable {
102 99
103 public String getOutermostClassName() { 100 public String getOutermostClassName() {
104 if (isInnerClass()) { 101 if (isInnerClass()) {
105 return m_name.substring(0, m_name.indexOf('$')); 102 return this.name.substring(0, this.name.indexOf('$'));
106 } 103 }
107 return m_name; 104 return this.name;
108 } 105 }
109 106
110 public ClassEntry getOutermostClassEntry() { 107 public ClassEntry getOutermostClassEntry() {
@@ -115,7 +112,7 @@ public class ClassEntry implements Entry, Serializable {
115 if (!isInnerClass()) { 112 if (!isInnerClass()) {
116 throw new Error("This is not an inner class!"); 113 throw new Error("This is not an inner class!");
117 } 114 }
118 return m_name.substring(0, m_name.lastIndexOf('$')); 115 return this.name.substring(0, this.name.lastIndexOf('$'));
119 } 116 }
120 117
121 public ClassEntry getOuterClassEntry() { 118 public ClassEntry getOuterClassEntry() {
@@ -126,27 +123,27 @@ public class ClassEntry implements Entry, Serializable {
126 if (!isInnerClass()) { 123 if (!isInnerClass()) {
127 throw new Error("This is not an inner class!"); 124 throw new Error("This is not an inner class!");
128 } 125 }
129 return m_name.substring(m_name.lastIndexOf('$') + 1); 126 return this.name.substring(this.name.lastIndexOf('$') + 1);
130 } 127 }
131 128
132 public boolean isInDefaultPackage() { 129 public boolean isInDefaultPackage() {
133 return m_name.indexOf('/') < 0; 130 return this.name.indexOf('/') < 0;
134 } 131 }
135 132
136 public String getPackageName() { 133 public String getPackageName() {
137 int pos = m_name.lastIndexOf('/'); 134 int pos = this.name.lastIndexOf('/');
138 if (pos > 0) { 135 if (pos > 0) {
139 return m_name.substring(0, pos); 136 return this.name.substring(0, pos);
140 } 137 }
141 return null; 138 return null;
142 } 139 }
143 140
144 public String getSimpleName() { 141 public String getSimpleName() {
145 int pos = m_name.lastIndexOf('/'); 142 int pos = this.name.lastIndexOf('/');
146 if (pos > 0) { 143 if (pos > 0) {
147 return m_name.substring(pos + 1); 144 return this.name.substring(pos + 1);
148 } 145 }
149 return m_name; 146 return this.name;
150 } 147 }
151 148
152 public ClassEntry buildClassEntry(List<ClassEntry> classChain) { 149 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
index f831a3b8..b2c076a3 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
@@ -12,13 +12,11 @@ package cuchaz.enigma.mapping;
12 12
13import com.google.common.collect.Maps; 13import com.google.common.collect.Maps;
14 14
15import java.io.Serializable;
16import java.util.ArrayList;
17import java.util.Map; 15import java.util.Map;
18 16
19public class ClassMapping implements Serializable, Comparable<ClassMapping> { 17import cuchaz.enigma.throwables.MappingConflict;
20 18
21 private static final long serialVersionUID = -5148491146902340107L; 19public class ClassMapping implements Comparable<ClassMapping> {
22 20
23 private String m_obfFullName; 21 private String m_obfFullName;
24 private String m_obfSimpleName; 22 private String m_obfSimpleName;
@@ -70,13 +68,17 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> {
70 return m_innerClassesByObfSimple.values(); 68 return m_innerClassesByObfSimple.values();
71 } 69 }
72 70
73 public void addInnerClassMapping(ClassMapping classMapping) { 71 public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict {
74 boolean obfWasAdded = m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; 72 if (this.m_innerClassesByObfSimple.containsKey(classMapping.getObfSimpleName())) {
75 assert (obfWasAdded); 73 throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.m_innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName());
74 }
75 m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping);
76
76 if (classMapping.getDeobfName() != null) { 77 if (classMapping.getDeobfName() != null) {
77 assert (isSimpleClassName(classMapping.getDeobfName())); 78 if (this.m_innerClassesByDeobf.containsKey(classMapping.getDeobfName())) {
78 boolean deobfWasAdded = m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; 79 throw new MappingConflict("classes", classMapping.getDeobfName(), this.m_innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
79 assert (deobfWasAdded); 80 }
81 m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping);
80 } 82 }
81 } 83 }
82 84
@@ -225,17 +227,6 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> {
225 } 227 }
226 } 228 }
227 229
228 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) {
229 assert (newObfName != null);
230 FieldMapping fieldMapping = m_fieldsByObf.remove(getFieldKey(oldObfName, obfType));
231 assert (fieldMapping != null);
232 fieldMapping.setObfName(newObfName);
233 fieldMapping.setObfType(newObfType);
234 boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null;
235 assert (obfWasAdded);
236 }
237
238
239 //// METHODS //////// 230 //// METHODS ////////
240 231
241 public Iterable<MethodMapping> methods() { 232 public Iterable<MethodMapping> methods() {
@@ -307,16 +298,6 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> {
307 } 298 }
308 } 299 }
309 300
310 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) {
311 assert (newObfName != null);
312 MethodMapping methodMapping = m_methodsByObf.remove(getMethodKey(oldObfName, obfSignature));
313 assert (methodMapping != null);
314 methodMapping.setObfName(newObfName);
315 methodMapping.setObfSignature(newObfSignature);
316 boolean obfWasAdded = m_methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null;
317 assert (obfWasAdded);
318 }
319
320 //// ARGUMENTS //////// 301 //// ARGUMENTS ////////
321 302
322 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { 303 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) {
@@ -388,7 +369,4 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> {
388 return name.indexOf('/') < 0 && name.indexOf('$') < 0; 369 return name.indexOf('/') < 0 && name.indexOf('$') < 0;
389 } 370 }
390 371
391 public ClassEntry getObfEntry() {
392 return new ClassEntry(m_obfFullName);
393 }
394} 372}
diff --git a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
index ac1a7f2b..4c798204 100644
--- a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
@@ -10,16 +10,12 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.io.Serializable; 13import cuchaz.enigma.utils.Utils;
14 14
15import cuchaz.enigma.Util; 15public class ConstructorEntry implements BehaviorEntry {
16 16
17public class ConstructorEntry implements BehaviorEntry, Serializable { 17 private ClassEntry classEntry;
18 18 private Signature signature;
19 private static final long serialVersionUID = -868346075317366758L;
20
21 private ClassEntry m_classEntry;
22 private Signature m_signature;
23 19
24 public ConstructorEntry(ClassEntry classEntry) { 20 public ConstructorEntry(ClassEntry classEntry) {
25 this(classEntry, null); 21 this(classEntry, null);
@@ -30,23 +26,18 @@ public class ConstructorEntry implements BehaviorEntry, Serializable {
30 throw new IllegalArgumentException("Class cannot be null!"); 26 throw new IllegalArgumentException("Class cannot be null!");
31 } 27 }
32 28
33 m_classEntry = classEntry; 29 this.classEntry = classEntry;
34 m_signature = signature; 30 this.signature = signature;
35 }
36
37 public ConstructorEntry(ConstructorEntry other) {
38 m_classEntry = new ClassEntry(other.m_classEntry);
39 m_signature = other.m_signature;
40 } 31 }
41 32
42 public ConstructorEntry(ConstructorEntry other, String newClassName) { 33 public ConstructorEntry(ConstructorEntry other, String newClassName) {
43 m_classEntry = new ClassEntry(newClassName); 34 this.classEntry = new ClassEntry(newClassName);
44 m_signature = other.m_signature; 35 this.signature = other.signature;
45 } 36 }
46 37
47 @Override 38 @Override
48 public ClassEntry getClassEntry() { 39 public ClassEntry getClassEntry() {
49 return m_classEntry; 40 return this.classEntry;
50 } 41 }
51 42
52 @Override 43 @Override
@@ -58,17 +49,17 @@ public class ConstructorEntry implements BehaviorEntry, Serializable {
58 } 49 }
59 50
60 public boolean isStatic() { 51 public boolean isStatic() {
61 return m_signature == null; 52 return this.signature == null;
62 } 53 }
63 54
64 @Override 55 @Override
65 public Signature getSignature() { 56 public Signature getSignature() {
66 return m_signature; 57 return this.signature;
67 } 58 }
68 59
69 @Override 60 @Override
70 public String getClassName() { 61 public String getClassName() {
71 return m_classEntry.getName(); 62 return this.classEntry.getName();
72 } 63 }
73 64
74 @Override 65 @Override
@@ -79,9 +70,9 @@ public class ConstructorEntry implements BehaviorEntry, Serializable {
79 @Override 70 @Override
80 public int hashCode() { 71 public int hashCode() {
81 if (isStatic()) { 72 if (isStatic()) {
82 return Util.combineHashesOrdered(m_classEntry); 73 return Utils.combineHashesOrdered(this.classEntry);
83 } else { 74 } else {
84 return Util.combineHashesOrdered(m_classEntry, m_signature); 75 return Utils.combineHashesOrdered(this.classEntry, this.signature);
85 } 76 }
86 } 77 }
87 78
@@ -96,18 +87,18 @@ public class ConstructorEntry implements BehaviorEntry, Serializable {
96 } 87 }
97 88
98 if (isStatic()) { 89 if (isStatic()) {
99 return m_classEntry.equals(other.m_classEntry); 90 return this.classEntry.equals(other.classEntry);
100 } else { 91 } else {
101 return m_classEntry.equals(other.m_classEntry) && m_signature.equals(other.m_signature); 92 return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature);
102 } 93 }
103 } 94 }
104 95
105 @Override 96 @Override
106 public String toString() { 97 public String toString() {
107 if (isStatic()) { 98 if (isStatic()) {
108 return m_classEntry.getName() + "." + getName(); 99 return this.classEntry.getName() + "." + getName();
109 } else { 100 } else {
110 return m_classEntry.getName() + "." + getName() + m_signature; 101 return this.classEntry.getName() + "." + getName() + this.signature;
111 } 102 }
112 } 103 }
113} 104}
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
index 3584ebbf..2351dcfb 100644
--- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
+++ b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
@@ -38,19 +38,11 @@ public class EntryFactory {
38 } 38 }
39 39
40 public static FieldEntry getFieldEntry(CtField field) { 40 public static FieldEntry getFieldEntry(CtField field) {
41 return new FieldEntry( 41 return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor()));
42 getClassEntry(field.getDeclaringClass()),
43 field.getName(),
44 new Type(field.getFieldInfo().getDescriptor())
45 );
46 } 42 }
47 43
48 public static FieldEntry getFieldEntry(FieldAccess call) { 44 public static FieldEntry getFieldEntry(FieldAccess call) {
49 return new FieldEntry( 45 return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature()));
50 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
51 call.getFieldName(),
52 new Type(call.getSignature())
53 );
54 } 46 }
55 47
56 public static FieldEntry getFieldEntry(String className, String name, String type) { 48 public static FieldEntry getFieldEntry(String className, String name, String type) {
@@ -58,19 +50,11 @@ public class EntryFactory {
58 } 50 }
59 51
60 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { 52 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) {
61 return new FieldEntry( 53 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType());
62 getObfClassEntry(classMapping),
63 fieldMapping.getObfName(),
64 fieldMapping.getObfType()
65 );
66 } 54 }
67 55
68 public static MethodEntry getMethodEntry(CtMethod method) { 56 public static MethodEntry getMethodEntry(CtMethod method) {
69 return new MethodEntry( 57 return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor()));
70 getClassEntry(method.getDeclaringClass()),
71 method.getName(),
72 new Signature(method.getMethodInfo().getDescriptor())
73 );
74 } 58 }
75 59
76 public static MethodEntry getMethodEntry(MethodCall call) { 60 public static MethodEntry getMethodEntry(MethodCall call) {
@@ -106,10 +90,6 @@ public class EntryFactory {
106 return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); 90 return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature));
107 } 91 }
108 92
109 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) {
110 return getBehaviorEntry(new ClassEntry(className), behaviorName);
111 }
112
113 public static BehaviorEntry getBehaviorEntry(String className) { 93 public static BehaviorEntry getBehaviorEntry(String className) {
114 return new ConstructorEntry(new ClassEntry(className)); 94 return new ConstructorEntry(new ClassEntry(className));
115 } 95 }
@@ -125,14 +105,6 @@ public class EntryFactory {
125 } 105 }
126 } 106 }
127 107
128 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) {
129 if (behaviorName.equals("<clinit>")) {
130 return new ConstructorEntry(classEntry);
131 } else {
132 throw new IllegalArgumentException("Only class initializers don't have signatures");
133 }
134 }
135
136 public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { 108 public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) {
137 return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); 109 return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature());
138 } 110 }
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
index bebc5045..9980e8e6 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
@@ -10,17 +10,13 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.io.Serializable; 13import cuchaz.enigma.utils.Utils;
14 14
15import cuchaz.enigma.Util; 15public class FieldEntry implements Entry {
16 16
17public class FieldEntry implements Entry, Serializable { 17 private ClassEntry classEntry;
18 18 private String name;
19 private static final long serialVersionUID = 3004663582802885451L; 19 private Type type;
20
21 private ClassEntry m_classEntry;
22 private String m_name;
23 private Type m_type;
24 20
25 // NOTE: this argument order is important for the MethodReader/MethodWriter 21 // NOTE: this argument order is important for the MethodReader/MethodWriter
26 public FieldEntry(ClassEntry classEntry, String name, Type type) { 22 public FieldEntry(ClassEntry classEntry, String name, Type type) {
@@ -34,38 +30,34 @@ public class FieldEntry implements Entry, Serializable {
34 throw new IllegalArgumentException("Field type cannot be null!"); 30 throw new IllegalArgumentException("Field type cannot be null!");
35 } 31 }
36 32
37 m_classEntry = classEntry; 33 this.classEntry = classEntry;
38 m_name = name; 34 this.name = name;
39 m_type = type; 35 this.type = type;
40 }
41
42 public FieldEntry(FieldEntry other) {
43 this(other, new ClassEntry(other.m_classEntry));
44 } 36 }
45 37
46 public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { 38 public FieldEntry(FieldEntry other, ClassEntry newClassEntry) {
47 m_classEntry = newClassEntry; 39 this.classEntry = newClassEntry;
48 m_name = other.m_name; 40 this.name = other.name;
49 m_type = other.m_type; 41 this.type = other.type;
50 } 42 }
51 43
52 @Override 44 @Override
53 public ClassEntry getClassEntry() { 45 public ClassEntry getClassEntry() {
54 return m_classEntry; 46 return this.classEntry;
55 } 47 }
56 48
57 @Override 49 @Override
58 public String getName() { 50 public String getName() {
59 return m_name; 51 return this.name;
60 } 52 }
61 53
62 @Override 54 @Override
63 public String getClassName() { 55 public String getClassName() {
64 return m_classEntry.getName(); 56 return this.classEntry.getName();
65 } 57 }
66 58
67 public Type getType() { 59 public Type getType() {
68 return m_type; 60 return this.type;
69 } 61 }
70 62
71 @Override 63 @Override
@@ -75,7 +67,7 @@ public class FieldEntry implements Entry, Serializable {
75 67
76 @Override 68 @Override
77 public int hashCode() { 69 public int hashCode() {
78 return Util.combineHashesOrdered(m_classEntry, m_name, m_type); 70 return Utils.combineHashesOrdered(this.classEntry, this.name, this.type);
79 } 71 }
80 72
81 @Override 73 @Override
@@ -84,13 +76,11 @@ public class FieldEntry implements Entry, Serializable {
84 } 76 }
85 77
86 public boolean equals(FieldEntry other) { 78 public boolean equals(FieldEntry other) {
87 return m_classEntry.equals(other.m_classEntry) 79 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type);
88 && m_name.equals(other.m_name)
89 && m_type.equals(other.m_type);
90 } 80 }
91 81
92 @Override 82 @Override
93 public String toString() { 83 public String toString() {
94 return m_classEntry.getName() + "." + m_name + ":" + m_type; 84 return this.classEntry.getName() + "." + this.name + ":" + this.type;
95 } 85 }
96} 86}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
index 19d68a97..3ec1af0d 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
@@ -10,11 +10,7 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.io.Serializable; 13public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> {
14
15public class FieldMapping implements Serializable, Comparable<FieldMapping>, MemberMapping<FieldEntry> {
16
17 private static final long serialVersionUID = 8610742471440861315L;
18 14
19 private String obfName; 15 private String obfName;
20 private String deobfName; 16 private String deobfName;
@@ -26,21 +22,11 @@ public class FieldMapping implements Serializable, Comparable<FieldMapping>, Mem
26 this.obfType = obfType; 22 this.obfType = obfType;
27 } 23 }
28 24
29 public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) {
30 this.obfName = other.obfName;
31 this.deobfName = other.deobfName;
32 this.obfType = new Type(other.obfType, obfClassNameReplacer);
33 }
34
35 @Override 25 @Override
36 public String getObfName() { 26 public String getObfName() {
37 return this.obfName; 27 return this.obfName;
38 } 28 }
39 29
40 public void setObfName(String val) {
41 this.obfName = NameValidator.validateFieldName(val);
42 }
43
44 public String getDeobfName() { 30 public String getDeobfName() {
45 return this.deobfName; 31 return this.deobfName;
46 } 32 }
@@ -53,34 +39,8 @@ public class FieldMapping implements Serializable, Comparable<FieldMapping>, Mem
53 return this.obfType; 39 return this.obfType;
54 } 40 }
55 41
56 public void setObfType(Type val) {
57 this.obfType = val;
58 }
59
60 @Override 42 @Override
61 public int compareTo(FieldMapping other) { 43 public int compareTo(FieldMapping other) {
62 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); 44 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType);
63 } 45 }
64
65 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
66
67 // rename obf classes in the type
68 Type newType = new Type(this.obfType, className -> {
69 if (className.equals(oldObfClassName)) {
70 return newObfClassName;
71 }
72 return null;
73 });
74
75 if (!newType.equals(this.obfType)) {
76 this.obfType = newType;
77 return true;
78 }
79 return false;
80 }
81
82 @Override
83 public FieldEntry getObfEntry(ClassEntry classEntry) {
84 return new FieldEntry(classEntry, this.obfName, new Type(this.obfType));
85 }
86} 46}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
index ee4c3023..b51e1a4c 100644
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java
@@ -12,85 +12,64 @@ package cuchaz.enigma.mapping;
12 12
13import com.google.common.collect.Lists; 13import com.google.common.collect.Lists;
14import com.google.common.collect.Maps; 14import com.google.common.collect.Maps;
15import com.google.common.collect.Sets;
16 15
17import java.io.Serializable; 16import java.util.Collection;
18import java.util.*; 17import java.util.List;
18import java.util.Map;
19 19
20import cuchaz.enigma.analysis.TranslationIndex; 20import cuchaz.enigma.analysis.TranslationIndex;
21import cuchaz.enigma.throwables.MappingConflict;
21 22
22public class Mappings implements Serializable { 23public class Mappings {
23 24
24 private static final long serialVersionUID = 4649790259460259026L; 25 protected Map<String, ClassMapping> classesByObf;
25 26 protected Map<String, ClassMapping> classesByDeobf;
26 protected Map<String, ClassMapping> m_classesByObf;
27 protected Map<String, ClassMapping> m_classesByDeobf;
28 27
29 public Mappings() { 28 public Mappings() {
30 m_classesByObf = Maps.newHashMap(); 29 this.classesByObf = Maps.newHashMap();
31 m_classesByDeobf = Maps.newHashMap(); 30 this.classesByDeobf = Maps.newHashMap();
32 }
33
34 public Mappings(Iterable<ClassMapping> classes) {
35 this();
36
37 for (ClassMapping classMapping : classes) {
38 m_classesByObf.put(classMapping.getObfFullName(), classMapping);
39 if (classMapping.getDeobfName() != null) {
40 m_classesByDeobf.put(classMapping.getDeobfName(), classMapping);
41 }
42 }
43 } 31 }
44 32
45 public Collection<ClassMapping> classes() { 33 public Collection<ClassMapping> classes() {
46 assert (m_classesByObf.size() >= m_classesByDeobf.size()); 34 assert (this.classesByObf.size() >= this.classesByDeobf.size());
47 return m_classesByObf.values(); 35 return this.classesByObf.values();
48 } 36 }
49 37
50 public void addClassMapping(ClassMapping classMapping) { 38 public void addClassMapping(ClassMapping classMapping) throws MappingConflict {
51 if (m_classesByObf.containsKey(classMapping.getObfFullName())) { 39 if (this.classesByObf.containsKey(classMapping.getObfFullName())) {
52 throw new Error("Already have mapping for " + classMapping.getObfFullName()); 40 throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName());
53 } 41 }
54 boolean obfWasAdded = m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; 42 this.classesByObf.put(classMapping.getObfFullName(), classMapping);
55 assert (obfWasAdded); 43
56 if (classMapping.getDeobfName() != null) { 44 if (classMapping.getDeobfName() != null) {
57 if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { 45 if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) {
58 throw new Error("Already have mapping for " + classMapping.getDeobfName()); 46 throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
59 } 47 }
60 boolean deobfWasAdded = m_classesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; 48 this.classesByDeobf.put(classMapping.getDeobfName(), classMapping);
61 assert (deobfWasAdded);
62 } 49 }
63 } 50 }
64 51
65 public void removeClassMapping(ClassMapping classMapping) { 52 public void removeClassMapping(ClassMapping classMapping) {
66 boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfFullName()) != null; 53 boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null;
67 assert (obfWasRemoved); 54 assert (obfWasRemoved);
68 if (classMapping.getDeobfName() != null) { 55 if (classMapping.getDeobfName() != null) {
69 boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; 56 boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
70 assert (deobfWasRemoved); 57 assert (deobfWasRemoved);
71 } 58 }
72 } 59 }
73 60
74 public ClassMapping getClassByObf(ClassEntry entry) {
75 return getClassByObf(entry.getName());
76 }
77
78 public ClassMapping getClassByObf(String obfName) { 61 public ClassMapping getClassByObf(String obfName) {
79 return m_classesByObf.get(obfName); 62 return this.classesByObf.get(obfName);
80 }
81
82 public ClassMapping getClassByDeobf(String deobfName) {
83 return m_classesByDeobf.get(deobfName);
84 } 63 }
85 64
86 public void setClassDeobfName(ClassMapping classMapping, String deobfName) { 65 public void setClassDeobfName(ClassMapping classMapping, String deobfName) {
87 if (classMapping.getDeobfName() != null) { 66 if (classMapping.getDeobfName() != null) {
88 boolean wasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; 67 boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
89 assert (wasRemoved); 68 assert (wasRemoved);
90 } 69 }
91 classMapping.setDeobfName(deobfName); 70 classMapping.setDeobfName(deobfName);
92 if (deobfName != null) { 71 if (deobfName != null) {
93 boolean wasAdded = m_classesByDeobf.put(deobfName, classMapping) == null; 72 boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null;
94 assert (wasAdded); 73 assert (wasAdded);
95 } 74 }
96 } 75 }
@@ -99,7 +78,7 @@ public class Mappings implements Serializable {
99 switch (direction) { 78 switch (direction) {
100 case Deobfuscating: 79 case Deobfuscating:
101 80
102 return new Translator(direction, m_classesByObf, index); 81 return new Translator(direction, this.classesByObf, index);
103 82
104 case Obfuscating: 83 case Obfuscating:
105 84
@@ -127,7 +106,7 @@ public class Mappings implements Serializable {
127 @Override 106 @Override
128 public String toString() { 107 public String toString() {
129 StringBuilder buf = new StringBuilder(); 108 StringBuilder buf = new StringBuilder();
130 for (ClassMapping classMapping : m_classesByObf.values()) { 109 for (ClassMapping classMapping : this.classesByObf.values()) {
131 buf.append(classMapping.toString()); 110 buf.append(classMapping.toString());
132 buf.append("\n"); 111 buf.append("\n");
133 } 112 }
@@ -135,21 +114,21 @@ public class Mappings implements Serializable {
135 } 114 }
136 115
137 public boolean containsDeobfClass(String deobfName) { 116 public boolean containsDeobfClass(String deobfName) {
138 return m_classesByDeobf.containsKey(deobfName); 117 return this.classesByDeobf.containsKey(deobfName);
139 } 118 }
140 119
141 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { 120 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) {
142 ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); 121 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
143 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); 122 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType);
144 } 123 }
145 124
146 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature deobfSignature) { 125 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature deobfSignature) {
147 ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); 126 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
148 return classMapping != null && classMapping.containsDeobfMethod(deobfName, deobfSignature); 127 return classMapping != null && classMapping.containsDeobfMethod(deobfName, deobfSignature);
149 } 128 }
150 129
151 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 130 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) {
152 ClassMapping classMapping = m_classesByObf.get(obfBehaviorEntry.getClassName()); 131 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName());
153 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); 132 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name);
154 } 133 }
155 134
@@ -158,7 +137,7 @@ public class Mappings implements Serializable {
158 ClassMapping classMapping = null; 137 ClassMapping classMapping = null;
159 for (ClassEntry obfClassEntry : obfClass.getClassChain()) { 138 for (ClassEntry obfClassEntry : obfClass.getClassChain()) {
160 if (mappingChain.isEmpty()) { 139 if (mappingChain.isEmpty()) {
161 classMapping = m_classesByObf.get(obfClassEntry.getName()); 140 classMapping = this.classesByObf.get(obfClassEntry.getName());
162 } else if (classMapping != null) { 141 } else if (classMapping != null) {
163 classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); 142 classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName());
164 } 143 }
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
index d8507877..6cf279d1 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
@@ -20,41 +20,41 @@ import cuchaz.enigma.analysis.JarIndex;
20 20
21public class MappingsChecker { 21public class MappingsChecker {
22 22
23 private JarIndex m_index; 23 private JarIndex index;
24 private Map<ClassEntry, ClassMapping> m_droppedClassMappings; 24 private Map<ClassEntry, ClassMapping> droppedClassMappings;
25 private Map<ClassEntry, ClassMapping> m_droppedInnerClassMappings; 25 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings;
26 private Map<FieldEntry, FieldMapping> m_droppedFieldMappings; 26 private Map<FieldEntry, FieldMapping> droppedFieldMappings;
27 private Map<BehaviorEntry, MethodMapping> m_droppedMethodMappings; 27 private Map<BehaviorEntry, MethodMapping> droppedMethodMappings;
28 28
29 public MappingsChecker(JarIndex index) { 29 public MappingsChecker(JarIndex index) {
30 m_index = index; 30 this.index = index;
31 m_droppedClassMappings = Maps.newHashMap(); 31 this.droppedClassMappings = Maps.newHashMap();
32 m_droppedInnerClassMappings = Maps.newHashMap(); 32 this.droppedInnerClassMappings = Maps.newHashMap();
33 m_droppedFieldMappings = Maps.newHashMap(); 33 this.droppedFieldMappings = Maps.newHashMap();
34 m_droppedMethodMappings = Maps.newHashMap(); 34 this.droppedMethodMappings = Maps.newHashMap();
35 } 35 }
36 36
37 public Map<ClassEntry, ClassMapping> getDroppedClassMappings() { 37 public Map<ClassEntry, ClassMapping> getDroppedClassMappings() {
38 return m_droppedClassMappings; 38 return this.droppedClassMappings;
39 } 39 }
40 40
41 public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() { 41 public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() {
42 return m_droppedInnerClassMappings; 42 return this.droppedInnerClassMappings;
43 } 43 }
44 44
45 public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() { 45 public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() {
46 return m_droppedFieldMappings; 46 return this.droppedFieldMappings;
47 } 47 }
48 48
49 public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() { 49 public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() {
50 return m_droppedMethodMappings; 50 return this.droppedMethodMappings;
51 } 51 }
52 52
53 public void dropBrokenMappings(Mappings mappings) { 53 public void dropBrokenMappings(Mappings mappings) {
54 for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { 54 for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) {
55 if (!checkClassMapping(classMapping)) { 55 if (!checkClassMapping(classMapping)) {
56 mappings.removeClassMapping(classMapping); 56 mappings.removeClassMapping(classMapping);
57 m_droppedClassMappings.put(EntryFactory.getObfClassEntry(m_index, classMapping), classMapping); 57 this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping);
58 } 58 }
59 } 59 }
60 } 60 }
@@ -62,26 +62,26 @@ public class MappingsChecker {
62 private boolean checkClassMapping(ClassMapping classMapping) { 62 private boolean checkClassMapping(ClassMapping classMapping) {
63 63
64 // check the class 64 // check the class
65 ClassEntry classEntry = EntryFactory.getObfClassEntry(m_index, classMapping); 65 ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping);
66 if (!m_index.getObfClassEntries().contains(classEntry)) { 66 if (!this.index.getObfClassEntries().contains(classEntry)) {
67 return false; 67 return false;
68 } 68 }
69 69
70 // check the fields 70 // check the fields
71 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { 71 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) {
72 FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); 72 FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping);
73 if (!m_index.containsObfField(obfFieldEntry)) { 73 if (!this.index.containsObfField(obfFieldEntry)) {
74 classMapping.removeFieldMapping(fieldMapping); 74 classMapping.removeFieldMapping(fieldMapping);
75 m_droppedFieldMappings.put(obfFieldEntry, fieldMapping); 75 this.droppedFieldMappings.put(obfFieldEntry, fieldMapping);
76 } 76 }
77 } 77 }
78 78
79 // check methods 79 // check methods
80 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 80 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
81 BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); 81 BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping);
82 if (!m_index.containsObfBehavior(obfBehaviorEntry)) { 82 if (!this.index.containsObfBehavior(obfBehaviorEntry)) {
83 classMapping.removeMethodMapping(methodMapping); 83 classMapping.removeMethodMapping(methodMapping);
84 m_droppedMethodMappings.put(obfBehaviorEntry, methodMapping); 84 this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping);
85 } 85 }
86 } 86 }
87 87
@@ -89,7 +89,7 @@ public class MappingsChecker {
89 for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { 89 for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) {
90 if (!checkClassMapping(innerClassMapping)) { 90 if (!checkClassMapping(innerClassMapping)) {
91 classMapping.removeInnerClassMapping(innerClassMapping); 91 classMapping.removeInnerClassMapping(innerClassMapping);
92 m_droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(m_index, innerClassMapping), innerClassMapping); 92 this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping);
93 } 93 }
94 } 94 }
95 95
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsReader.java
index 7aedc5b1..b2b6d09c 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsReader.java
@@ -20,17 +20,17 @@ import java.io.FileReader;
20import java.io.IOException; 20import java.io.IOException;
21 21
22import cuchaz.enigma.json.JsonClass; 22import cuchaz.enigma.json.JsonClass;
23import cuchaz.enigma.throwables.MappingConflict;
23 24
24public class MappingsReader { 25public class MappingsReader {
25 26
26 public Mappings read(File in) throws IOException, MappingParseException { 27 public Mappings read(File in) throws IOException {
27 Mappings mappings = new Mappings(); 28 Mappings mappings = new Mappings();
28 readDirectory(mappings, in); 29 readDirectory(mappings, in);
29 return mappings; 30 return mappings;
30 } 31 }
31 32
32 public void readDirectory(Mappings mappings, File in) throws IOException, MappingParseException { 33 public void readDirectory(Mappings mappings, File in) throws IOException {
33
34 File[] fList = in.listFiles(); 34 File[] fList = in.listFiles();
35 if (fList != null) { 35 if (fList != null) {
36 for (File file : fList) { 36 for (File file : fList) {
@@ -43,8 +43,7 @@ public class MappingsReader {
43 } 43 }
44 } 44 }
45 45
46 public void readFile(Mappings mappings, BufferedReader in) throws IOException, MappingParseException { 46 public void readFile(Mappings mappings, BufferedReader in) throws IOException {
47
48 StringBuilder buf = new StringBuilder(); 47 StringBuilder buf = new StringBuilder();
49 String line; 48 String line;
50 while ((line = in.readLine()) != null) { 49 while ((line = in.readLine()) != null) {
@@ -53,11 +52,15 @@ public class MappingsReader {
53 52
54 Gson gson = new GsonBuilder().setPrettyPrinting().create(); 53 Gson gson = new GsonBuilder().setPrettyPrinting().create();
55 JsonClass jsonClass = gson.fromJson(buf.toString(), JsonClass.class); 54 JsonClass jsonClass = gson.fromJson(buf.toString(), JsonClass.class);
56 load(null, jsonClass, mappings); 55 try {
56 load(null, jsonClass, mappings);
57 } catch (MappingConflict e) {
58 e.printStackTrace();
59 }
57 in.close(); 60 in.close();
58 } 61 }
59 62
60 public void load(ClassMapping parent, JsonClass jsonClass, Mappings mappings) { 63 public void load(ClassMapping parent, JsonClass jsonClass, Mappings mappings) throws MappingConflict {
61 ClassMapping classMapping = readClass(jsonClass.getObf(), jsonClass.getName()); 64 ClassMapping classMapping = readClass(jsonClass.getObf(), jsonClass.getName());
62 if (parent != null) { 65 if (parent != null) {
63 parent.addInnerClassMapping(classMapping); 66 parent.addInnerClassMapping(classMapping);
@@ -68,17 +71,35 @@ public class MappingsReader {
68 71
69 jsonClass.getConstructors().forEach(jsonConstructor -> { 72 jsonClass.getConstructors().forEach(jsonConstructor -> {
70 MethodMapping methodMapping = readMethod(jsonConstructor.isStatics() ? "<clinit>" : "<init>", null, jsonConstructor.getSignature()); 73 MethodMapping methodMapping = readMethod(jsonConstructor.isStatics() ? "<clinit>" : "<init>", null, jsonConstructor.getSignature());
71 jsonConstructor.getArgs().forEach(jsonArgument -> methodMapping.addArgumentMapping(readArgument(jsonArgument.getIndex(), jsonArgument.getName()))); 74 jsonConstructor.getArgs().forEach(jsonArgument -> {
75 try {
76 methodMapping.addArgumentMapping(readArgument(jsonArgument.getIndex(), jsonArgument.getName()));
77 } catch (MappingConflict e) {
78 e.printStackTrace();
79 }
80 });
72 classMapping.addMethodMapping(methodMapping); 81 classMapping.addMethodMapping(methodMapping);
73 }); 82 });
74 83
75 jsonClass.getMethod().forEach(jsonMethod -> { 84 jsonClass.getMethod().forEach(jsonMethod -> {
76 MethodMapping methodMapping = readMethod(jsonMethod.getObf(), jsonMethod.getName(), jsonMethod.getSignature()); 85 MethodMapping methodMapping = readMethod(jsonMethod.getObf(), jsonMethod.getName(), jsonMethod.getSignature());
77 jsonMethod.getArgs().forEach(jsonArgument -> methodMapping.addArgumentMapping(readArgument(jsonArgument.getIndex(), jsonArgument.getName()))); 86 jsonMethod.getArgs().forEach(jsonArgument -> {
87 try {
88 methodMapping.addArgumentMapping(readArgument(jsonArgument.getIndex(), jsonArgument.getName()));
89 } catch (MappingConflict e) {
90 e.printStackTrace();
91 }
92 });
78 classMapping.addMethodMapping(methodMapping); 93 classMapping.addMethodMapping(methodMapping);
79 }); 94 });
80 95
81 jsonClass.getInnerClass().forEach(jsonInnerClasses -> load(classMapping, jsonInnerClasses, mappings)); 96 jsonClass.getInnerClass().forEach(jsonInnerClasses -> {
97 try {
98 load(classMapping, jsonInnerClasses, mappings);
99 } catch (MappingConflict e) {
100 e.printStackTrace();
101 }
102 });
82 } 103 }
83 104
84 private ArgumentMapping readArgument(int index, String name) { 105 private ArgumentMapping readArgument(int index, String name) {
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsReaderOld.java b/src/main/java/cuchaz/enigma/mapping/MappingsReaderOld.java
index ed36d423..776d9083 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsReaderOld.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsReaderOld.java
@@ -7,6 +7,9 @@ import java.io.IOException;
7import java.io.Reader; 7import java.io.Reader;
8import java.util.Deque; 8import java.util.Deque;
9 9
10import cuchaz.enigma.throwables.MappingConflict;
11import cuchaz.enigma.throwables.MappingParseException;
12
10public class MappingsReaderOld { 13public class MappingsReaderOld {
11 14
12 public Mappings read(Reader in) throws IOException, MappingParseException { 15 public Mappings read(Reader in) throws IOException, MappingParseException {
@@ -89,6 +92,8 @@ public class MappingsReaderOld {
89 } 92 }
90 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { 93 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
91 throw new MappingParseException(lineNumber, "Malformed line:\n" + line); 94 throw new MappingParseException(lineNumber, "Malformed line:\n" + line);
95 } catch (MappingConflict e) {
96 e.printStackTrace();
92 } 97 }
93 } 98 }
94 99
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
index 3050da6b..afb8c978 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -10,14 +10,12 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.io.IOException;
14import java.io.ObjectOutputStream;
15import java.io.OutputStream;
16import java.util.List; 13import java.util.List;
17import java.util.Set; 14import java.util.Set;
18import java.util.zip.GZIPOutputStream;
19 15
20import cuchaz.enigma.analysis.JarIndex; 16import cuchaz.enigma.analysis.JarIndex;
17import cuchaz.enigma.throwables.IllegalNameException;
18import cuchaz.enigma.throwables.MappingConflict;
21 19
22public class MappingsRenamer { 20public class MappingsRenamer {
23 21
@@ -167,14 +165,6 @@ public class MappingsRenamer {
167 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); 165 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName());
168 } 166 }
169 167
170 public void write(OutputStream out) throws IOException {
171 // TEMP: just use the object output for now. We can find a more efficient storage format later
172 GZIPOutputStream gzipout = new GZIPOutputStream(out);
173 ObjectOutputStream oout = new ObjectOutputStream(gzipout);
174 oout.writeObject(this);
175 gzipout.finish();
176 }
177
178 private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { 168 private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) {
179 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry); 169 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry);
180 return mappingChain.get(mappingChain.size() - 1); 170 return mappingChain.get(mappingChain.size() - 1);
@@ -193,10 +183,14 @@ public class MappingsRenamer {
193 mappingChain.set(i, classMapping); 183 mappingChain.set(i, classMapping);
194 184
195 // add it to the right parent 185 // add it to the right parent
196 if (i == 0) { 186 try {
197 m_mappings.addClassMapping(classMapping); 187 if (i == 0) {
198 } else { 188 m_mappings.addClassMapping(classMapping);
199 mappingChain.get(i - 1).addInnerClassMapping(classMapping); 189 } else {
190 mappingChain.get(i - 1).addInnerClassMapping(classMapping);
191 }
192 } catch (MappingConflict mappingConflict) {
193 mappingConflict.printStackTrace();
200 } 194 }
201 } 195 }
202 } 196 }
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsWriter.java
index 4b2db9dc..47931662 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsWriter.java
@@ -51,7 +51,7 @@ public class MappingsWriter {
51 } 51 }
52 } 52 }
53 53
54 private void write(JsonClass jsonClass, ClassMapping classMapping) throws IOException { 54 private void write(JsonClass jsonClass, ClassMapping classMapping) {
55 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { 55 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
56 JsonClass innerClass = new JsonClass(classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName().replace("nome/", ""), innerClassMapping.getDeobfName()); 56 JsonClass innerClass = new JsonClass(classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName().replace("nome/", ""), innerClassMapping.getDeobfName());
57 write(innerClass, innerClassMapping); 57 write(innerClass, innerClassMapping);
@@ -85,19 +85,18 @@ public class MappingsWriter {
85 } 85 }
86 86
87 public static boolean deleteDirectory(File directory) { 87 public static boolean deleteDirectory(File directory) {
88 if(directory.exists()){ 88 if (directory.exists()) {
89 File[] files = directory.listFiles(); 89 File[] files = directory.listFiles();
90 if(null!=files){ 90 if (null != files) {
91 for(int i=0; i<files.length; i++) { 91 for (int i = 0; i < files.length; i++) {
92 if(files[i].isDirectory()) { 92 if (files[i].isDirectory()) {
93 deleteDirectory(files[i]); 93 deleteDirectory(files[i]);
94 } 94 } else {
95 else {
96 files[i].delete(); 95 files[i].delete();
97 } 96 }
98 } 97 }
99 } 98 }
100 } 99 }
101 return(directory.delete()); 100 return (directory.delete());
102 } 101 }
103} 102}
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
index 90c096fa..590c830a 100644
--- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
@@ -12,7 +12,5 @@ package cuchaz.enigma.mapping;
12 12
13 13
14public interface MemberMapping<T extends Entry> { 14public interface MemberMapping<T extends Entry> {
15 T getObfEntry(ClassEntry classEntry);
16
17 String getObfName(); 15 String getObfName();
18} 16}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
index 7df4dd86..4d7ed8f0 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
@@ -10,17 +10,13 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.io.Serializable; 13import cuchaz.enigma.utils.Utils;
14 14
15import cuchaz.enigma.Util; 15public class MethodEntry implements BehaviorEntry {
16 16
17public class MethodEntry implements BehaviorEntry, Serializable { 17 private ClassEntry classEntry;
18 18 private String name;
19 private static final long serialVersionUID = 4770915224467247458L; 19 private Signature signature;
20
21 private ClassEntry m_classEntry;
22 private String m_name;
23 private Signature m_signature;
24 20
25 public MethodEntry(ClassEntry classEntry, String name, Signature signature) { 21 public MethodEntry(ClassEntry classEntry, String name, Signature signature) {
26 if (classEntry == null) { 22 if (classEntry == null) {
@@ -36,41 +32,35 @@ public class MethodEntry implements BehaviorEntry, Serializable {
36 throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); 32 throw new IllegalArgumentException("Don't use MethodEntry for a constructor!");
37 } 33 }
38 34
39 m_classEntry = classEntry; 35 this.classEntry = classEntry;
40 m_name = name; 36 this.name = name;
41 m_signature = signature; 37 this.signature = signature;
42 }
43
44 public MethodEntry(MethodEntry other) {
45 m_classEntry = new ClassEntry(other.m_classEntry);
46 m_name = other.m_name;
47 m_signature = other.m_signature;
48 } 38 }
49 39
50 public MethodEntry(MethodEntry other, String newClassName) { 40 public MethodEntry(MethodEntry other, String newClassName) {
51 m_classEntry = new ClassEntry(newClassName); 41 this.classEntry = new ClassEntry(newClassName);
52 m_name = other.m_name; 42 this.name = other.name;
53 m_signature = other.m_signature; 43 this.signature = other.signature;
54 } 44 }
55 45
56 @Override 46 @Override
57 public ClassEntry getClassEntry() { 47 public ClassEntry getClassEntry() {
58 return m_classEntry; 48 return this.classEntry;
59 } 49 }
60 50
61 @Override 51 @Override
62 public String getName() { 52 public String getName() {
63 return m_name; 53 return this.name;
64 } 54 }
65 55
66 @Override 56 @Override
67 public Signature getSignature() { 57 public Signature getSignature() {
68 return m_signature; 58 return this.signature;
69 } 59 }
70 60
71 @Override 61 @Override
72 public String getClassName() { 62 public String getClassName() {
73 return m_classEntry.getName(); 63 return this.classEntry.getName();
74 } 64 }
75 65
76 @Override 66 @Override
@@ -80,7 +70,7 @@ public class MethodEntry implements BehaviorEntry, Serializable {
80 70
81 @Override 71 @Override
82 public int hashCode() { 72 public int hashCode() {
83 return Util.combineHashesOrdered(m_classEntry, m_name, m_signature); 73 return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature);
84 } 74 }
85 75
86 @Override 76 @Override
@@ -89,11 +79,11 @@ public class MethodEntry implements BehaviorEntry, Serializable {
89 } 79 }
90 80
91 public boolean equals(MethodEntry other) { 81 public boolean equals(MethodEntry other) {
92 return m_classEntry.equals(other.m_classEntry) && m_name.equals(other.m_name) && m_signature.equals(other.m_signature); 82 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature);
93 } 83 }
94 84
95 @Override 85 @Override
96 public String toString() { 86 public String toString() {
97 return m_classEntry.getName() + "." + m_name + m_signature; 87 return this.classEntry.getName() + "." + this.name + this.signature;
98 } 88 }
99} 89}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
index fbd97bd1..6e7c1ef9 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
@@ -12,18 +12,16 @@ package cuchaz.enigma.mapping;
12 12
13import com.google.common.collect.Maps; 13import com.google.common.collect.Maps;
14 14
15import java.io.Serializable;
16import java.util.Map; 15import java.util.Map;
17import java.util.Map.Entry;
18 16
19public class MethodMapping implements Serializable, Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { 17import cuchaz.enigma.throwables.MappingConflict;
20 18
21 private static final long serialVersionUID = -4409570216084263978L; 19public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> {
22 20
23 private String m_obfName; 21 private String obfName;
24 private String m_deobfName; 22 private String deobfName;
25 private Signature m_obfSignature; 23 private Signature obfSignature;
26 private Map<Integer, ArgumentMapping> m_arguments; 24 private Map<Integer, ArgumentMapping> arguments;
27 25
28 public MethodMapping(String obfName, Signature obfSignature) { 26 public MethodMapping(String obfName, Signature obfSignature) {
29 this(obfName, obfSignature, null); 27 this(obfName, obfSignature, null);
@@ -36,62 +34,42 @@ public class MethodMapping implements Serializable, Comparable<MethodMapping>, M
36 if (obfSignature == null) { 34 if (obfSignature == null) {
37 throw new IllegalArgumentException("obf signature cannot be null!"); 35 throw new IllegalArgumentException("obf signature cannot be null!");
38 } 36 }
39 this.m_obfName = obfName; 37 this.obfName = obfName;
40 this.m_deobfName = NameValidator.validateMethodName(deobfName); 38 this.deobfName = NameValidator.validateMethodName(deobfName);
41 this.m_obfSignature = obfSignature; 39 this.obfSignature = obfSignature;
42 this.m_arguments = Maps.newTreeMap(); 40 this.arguments = Maps.newTreeMap();
43 }
44
45 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) {
46 this.m_obfName = other.m_obfName;
47 this.m_deobfName = other.m_deobfName;
48 this.m_obfSignature = new Signature(other.m_obfSignature, obfClassNameReplacer);
49 this.m_arguments = Maps.newTreeMap();
50 for (Entry<Integer, ArgumentMapping> entry : other.m_arguments.entrySet()) {
51 this.m_arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue()));
52 }
53 } 41 }
54 42
55 @Override 43 @Override
56 public String getObfName() { 44 public String getObfName() {
57 return this.m_obfName; 45 return this.obfName;
58 }
59
60 public void setObfName(String val) {
61 this.m_obfName = NameValidator.validateMethodName(val);
62 } 46 }
63 47
64 public String getDeobfName() { 48 public String getDeobfName() {
65 return this.m_deobfName; 49 return this.deobfName;
66 } 50 }
67 51
68 public void setDeobfName(String val) { 52 public void setDeobfName(String val) {
69 this.m_deobfName = NameValidator.validateMethodName(val); 53 this.deobfName = NameValidator.validateMethodName(val);
70 } 54 }
71 55
72 public Signature getObfSignature() { 56 public Signature getObfSignature() {
73 return this.m_obfSignature; 57 return this.obfSignature;
74 }
75
76 public void setObfSignature(Signature val) {
77 this.m_obfSignature = val;
78 } 58 }
79 59
80 public Iterable<ArgumentMapping> arguments() { 60 public Iterable<ArgumentMapping> arguments() {
81 return this.m_arguments.values(); 61 return this.arguments.values();
82 } 62 }
83 63
84 public boolean isConstructor() { 64 public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict {
85 return this.m_obfName.startsWith("<"); 65 if (this.arguments.containsKey(argumentMapping.getIndex())) {
86 } 66 throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName());
87 67 }
88 public void addArgumentMapping(ArgumentMapping argumentMapping) { 68 this.arguments.put(argumentMapping.getIndex(), argumentMapping);
89 boolean wasAdded = this.m_arguments.put(argumentMapping.getIndex(), argumentMapping) == null;
90 assert (wasAdded);
91 } 69 }
92 70
93 public String getObfArgumentName(int index) { 71 public String getObfArgumentName(int index) {
94 ArgumentMapping argumentMapping = this.m_arguments.get(index); 72 ArgumentMapping argumentMapping = this.arguments.get(index);
95 if (argumentMapping != null) { 73 if (argumentMapping != null) {
96 return argumentMapping.getName(); 74 return argumentMapping.getName();
97 } 75 }
@@ -100,7 +78,7 @@ public class MethodMapping implements Serializable, Comparable<MethodMapping>, M
100 } 78 }
101 79
102 public String getDeobfArgumentName(int index) { 80 public String getDeobfArgumentName(int index) {
103 ArgumentMapping argumentMapping = this.m_arguments.get(index); 81 ArgumentMapping argumentMapping = this.arguments.get(index);
104 if (argumentMapping != null) { 82 if (argumentMapping != null) {
105 return argumentMapping.getName(); 83 return argumentMapping.getName();
106 } 84 }
@@ -109,10 +87,10 @@ public class MethodMapping implements Serializable, Comparable<MethodMapping>, M
109 } 87 }
110 88
111 public void setArgumentName(int index, String name) { 89 public void setArgumentName(int index, String name) {
112 ArgumentMapping argumentMapping = this.m_arguments.get(index); 90 ArgumentMapping argumentMapping = this.arguments.get(index);
113 if (argumentMapping == null) { 91 if (argumentMapping == null) {
114 argumentMapping = new ArgumentMapping(index, name); 92 argumentMapping = new ArgumentMapping(index, name);
115 boolean wasAdded = this.m_arguments.put(index, argumentMapping) == null; 93 boolean wasAdded = this.arguments.put(index, argumentMapping) == null;
116 assert (wasAdded); 94 assert (wasAdded);
117 } else { 95 } else {
118 argumentMapping.setName(name); 96 argumentMapping.setName(name);
@@ -120,7 +98,7 @@ public class MethodMapping implements Serializable, Comparable<MethodMapping>, M
120 } 98 }
121 99
122 public void removeArgumentName(int index) { 100 public void removeArgumentName(int index) {
123 boolean wasRemoved = this.m_arguments.remove(index) != null; 101 boolean wasRemoved = this.arguments.remove(index) != null;
124 assert (wasRemoved); 102 assert (wasRemoved);
125 } 103 }
126 104
@@ -128,15 +106,15 @@ public class MethodMapping implements Serializable, Comparable<MethodMapping>, M
128 public String toString() { 106 public String toString() {
129 StringBuilder buf = new StringBuilder(); 107 StringBuilder buf = new StringBuilder();
130 buf.append("\t"); 108 buf.append("\t");
131 buf.append(m_obfName); 109 buf.append(this.obfName);
132 buf.append(" <-> "); 110 buf.append(" <-> ");
133 buf.append(m_deobfName); 111 buf.append(this.deobfName);
134 buf.append("\n"); 112 buf.append("\n");
135 buf.append("\t"); 113 buf.append("\t");
136 buf.append(m_obfSignature); 114 buf.append(this.obfSignature);
137 buf.append("\n"); 115 buf.append("\n");
138 buf.append("\tArguments:\n"); 116 buf.append("\tArguments:\n");
139 for (ArgumentMapping argumentMapping : this.m_arguments.values()) { 117 for (ArgumentMapping argumentMapping : this.arguments.values()) {
140 buf.append("\t\t"); 118 buf.append("\t\t");
141 buf.append(argumentMapping.getIndex()); 119 buf.append(argumentMapping.getIndex());
142 buf.append(" -> "); 120 buf.append(" -> ");
@@ -148,24 +126,15 @@ public class MethodMapping implements Serializable, Comparable<MethodMapping>, M
148 126
149 @Override 127 @Override
150 public int compareTo(MethodMapping other) { 128 public int compareTo(MethodMapping other) {
151 return (this.m_obfName + this.m_obfSignature).compareTo(other.m_obfName + other.m_obfSignature); 129 return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature);
152 } 130 }
153 131
154 public boolean containsArgument(String name) { 132 public boolean containsArgument(String name) {
155 for (ArgumentMapping argumentMapping : this.m_arguments.values()) { 133 for (ArgumentMapping argumentMapping : this.arguments.values()) {
156 if (argumentMapping.getName().equals(name)) { 134 if (argumentMapping.getName().equals(name)) {
157 return true; 135 return true;
158 } 136 }
159 } 137 }
160 return false; 138 return false;
161 } 139 }
162
163 @Override
164 public BehaviorEntry getObfEntry(ClassEntry classEntry) {
165 if (isConstructor()) {
166 return new ConstructorEntry(classEntry, this.m_obfSignature);
167 } else {
168 return new MethodEntry(classEntry, this.m_obfName, this.m_obfSignature);
169 }
170 }
171} 140}
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
index f416322b..15b0314c 100644
--- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java
+++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
@@ -14,6 +14,7 @@ import java.util.Arrays;
14import java.util.List; 14import java.util.List;
15import java.util.regex.Pattern; 15import java.util.regex.Pattern;
16 16
17import cuchaz.enigma.throwables.IllegalNameException;
17import javassist.bytecode.Descriptor; 18import javassist.bytecode.Descriptor;
18 19
19public class NameValidator { 20public class NameValidator {
diff --git a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
index ac424996..26e554b9 100644
--- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
+++ b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
@@ -13,35 +13,21 @@ package cuchaz.enigma.mapping;
13import com.strobel.assembler.metadata.FieldDefinition; 13import com.strobel.assembler.metadata.FieldDefinition;
14import com.strobel.assembler.metadata.MethodDefinition; 14import com.strobel.assembler.metadata.MethodDefinition;
15 15
16
17public class ProcyonEntryFactory { 16public class ProcyonEntryFactory {
18 17
19 public static FieldEntry getFieldEntry(FieldDefinition def) { 18 public static FieldEntry getFieldEntry(FieldDefinition def) {
20 return new FieldEntry( 19 return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature()));
21 new ClassEntry(def.getDeclaringType().getInternalName()),
22 def.getName(),
23 new Type(def.getErasedSignature())
24 );
25 } 20 }
26 21
27 public static MethodEntry getMethodEntry(MethodDefinition def) { 22 public static MethodEntry getMethodEntry(MethodDefinition def) {
28 return new MethodEntry( 23 return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(def.getErasedSignature()));
29 new ClassEntry(def.getDeclaringType().getInternalName()),
30 def.getName(),
31 new Signature(def.getErasedSignature())
32 );
33 } 24 }
34 25
35 public static ConstructorEntry getConstructorEntry(MethodDefinition def) { 26 public static ConstructorEntry getConstructorEntry(MethodDefinition def) {
36 if (def.isTypeInitializer()) { 27 if (def.isTypeInitializer()) {
37 return new ConstructorEntry( 28 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()));
38 new ClassEntry(def.getDeclaringType().getInternalName())
39 );
40 } else { 29 } else {
41 return new ConstructorEntry( 30 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature()));
42 new ClassEntry(def.getDeclaringType().getInternalName()),
43 new Signature(def.getErasedSignature())
44 );
45 } 31 }
46 } 32 }
47 33
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java
index e2f9f091..f30b6069 100644
--- a/src/main/java/cuchaz/enigma/mapping/Signature.java
+++ b/src/main/java/cuchaz/enigma/mapping/Signature.java
@@ -12,80 +12,72 @@ package cuchaz.enigma.mapping;
12 12
13import com.google.common.collect.Lists; 13import com.google.common.collect.Lists;
14 14
15import java.io.Serializable;
16import java.util.List; 15import java.util.List;
17 16
18import cuchaz.enigma.Util; 17import cuchaz.enigma.utils.Utils;
19 18
20public class Signature implements Serializable { 19public class Signature {
21 20
22 private static final long serialVersionUID = -5843719505729497539L; 21 private List<Type> argumentTypes;
23 22 private Type returnType;
24 private List<Type> m_argumentTypes;
25 private Type m_returnType;
26 23
27 public Signature(String signature) { 24 public Signature(String signature) {
28 try { 25 try {
29 m_argumentTypes = Lists.newArrayList(); 26 this.argumentTypes = Lists.newArrayList();
30 int i = 0; 27 int i = 0;
31 while (i < signature.length()) { 28 while (i < signature.length()) {
32 char c = signature.charAt(i); 29 char c = signature.charAt(i);
33 if (c == '(') { 30 if (c == '(') {
34 assert (m_argumentTypes.isEmpty()); 31 assert (this.argumentTypes.isEmpty());
35 assert (m_returnType == null); 32 assert (this.returnType == null);
36 i++; 33 i++;
37 } else if (c == ')') { 34 } else if (c == ')') {
38 i++; 35 i++;
39 break; 36 break;
40 } else { 37 } else {
41 String type = Type.parseFirst(signature.substring(i)); 38 String type = Type.parseFirst(signature.substring(i));
42 m_argumentTypes.add(new Type(type)); 39 this.argumentTypes.add(new Type(type));
43 i += type.length(); 40 i += type.length();
44 } 41 }
45 } 42 }
46 m_returnType = new Type(Type.parseFirst(signature.substring(i))); 43 this.returnType = new Type(Type.parseFirst(signature.substring(i)));
47 } catch (Exception ex) { 44 } catch (Exception ex) {
48 throw new IllegalArgumentException("Unable to parse signature: " + signature, ex); 45 throw new IllegalArgumentException("Unable to parse signature: " + signature, ex);
49 } 46 }
50 } 47 }
51 48
52 public Signature(Signature other) {
53 m_argumentTypes = Lists.newArrayList(other.m_argumentTypes);
54 m_returnType = new Type(other.m_returnType);
55 }
56
57 public Signature(Signature other, ClassNameReplacer replacer) { 49 public Signature(Signature other, ClassNameReplacer replacer) {
58 m_argumentTypes = Lists.newArrayList(other.m_argumentTypes); 50 this.argumentTypes = Lists.newArrayList(other.argumentTypes);
59 for (int i = 0; i < m_argumentTypes.size(); i++) { 51 for (int i = 0; i < this.argumentTypes.size(); i++) {
60 m_argumentTypes.set(i, new Type(m_argumentTypes.get(i), replacer)); 52 this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer));
61 } 53 }
62 m_returnType = new Type(other.m_returnType, replacer); 54 this.returnType = new Type(other.returnType, replacer);
63 } 55 }
64 56
65 public List<Type> getArgumentTypes() { 57 public List<Type> getArgumentTypes() {
66 return m_argumentTypes; 58 return this.argumentTypes;
67 } 59 }
68 60
69 public Type getReturnType() { 61 public Type getReturnType() {
70 return m_returnType; 62 return this.returnType;
71 } 63 }
72 64
73 @Override 65 @Override
74 public String toString() { 66 public String toString() {
75 StringBuilder buf = new StringBuilder(); 67 StringBuilder buf = new StringBuilder();
76 buf.append("("); 68 buf.append("(");
77 for (Type type : m_argumentTypes) { 69 for (Type type : this.argumentTypes) {
78 buf.append(type.toString()); 70 buf.append(type.toString());
79 } 71 }
80 buf.append(")"); 72 buf.append(")");
81 buf.append(m_returnType.toString()); 73 buf.append(this.returnType.toString());
82 return buf.toString(); 74 return buf.toString();
83 } 75 }
84 76
85 public Iterable<Type> types() { 77 public Iterable<Type> types() {
86 List<Type> types = Lists.newArrayList(); 78 List<Type> types = Lists.newArrayList();
87 types.addAll(m_argumentTypes); 79 types.addAll(this.argumentTypes);
88 types.add(m_returnType); 80 types.add(this.returnType);
89 return types; 81 return types;
90 } 82 }
91 83
@@ -95,12 +87,12 @@ public class Signature implements Serializable {
95 } 87 }
96 88
97 public boolean equals(Signature other) { 89 public boolean equals(Signature other) {
98 return m_argumentTypes.equals(other.m_argumentTypes) && m_returnType.equals(other.m_returnType); 90 return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType);
99 } 91 }
100 92
101 @Override 93 @Override
102 public int hashCode() { 94 public int hashCode() {
103 return Util.combineHashesOrdered(m_argumentTypes.hashCode(), m_returnType.hashCode()); 95 return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode());
104 } 96 }
105 97
106 public boolean hasClass(ClassEntry classEntry) { 98 public boolean hasClass(ClassEntry classEntry) {
diff --git a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java
deleted file mode 100644
index ec300fed..00000000
--- a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java
+++ /dev/null
@@ -1,82 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import com.google.common.collect.Lists;
14
15import java.io.IOException;
16import java.io.StringReader;
17import java.util.List;
18
19public class SignatureUpdater {
20
21 public interface ClassNameUpdater {
22 String update(String className);
23 }
24
25 public static String update(String signature, ClassNameUpdater updater) {
26 try {
27 StringBuilder buf = new StringBuilder();
28
29 // read the signature character-by-character
30 StringReader reader = new StringReader(signature);
31 int i;
32 while ((i = reader.read()) != -1) {
33 char c = (char) i;
34
35 // does this character start a class name?
36 if (c == 'L') {
37 // update the class name and add it to the buffer
38 buf.append('L');
39 String className = readClass(reader);
40 if (className == null) {
41 throw new IllegalArgumentException("Malformed signature: " + signature);
42 }
43 buf.append(updater.update(className));
44 buf.append(';');
45 } else {
46 // copy the character into the buffer
47 buf.append(c);
48 }
49 }
50
51 return buf.toString();
52 } catch (IOException ex) {
53 // I'm pretty sure a StringReader will never throw one of these
54 throw new Error(ex);
55 }
56 }
57
58 private static String readClass(StringReader reader) throws IOException {
59 // read all the characters in the buffer until we hit a ';'
60 // remember to treat generics correctly
61 StringBuilder buf = new StringBuilder();
62 int depth = 0;
63 int i;
64 while ((i = reader.read()) != -1) {
65 char c = (char) i;
66
67 if (c == '<') {
68 depth++;
69 } else if (c == '>') {
70 depth--;
71 } else if (depth == 0) {
72 if (c == ';') {
73 return buf.toString();
74 } else {
75 buf.append(c);
76 }
77 }
78 }
79
80 return null;
81 }
82}
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java
index b0107ce8..eb6a1892 100644
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ b/src/main/java/cuchaz/enigma/mapping/Translator.java
@@ -20,22 +20,22 @@ import cuchaz.enigma.analysis.TranslationIndex;
20 20
21public class Translator { 21public class Translator {
22 22
23 private TranslationDirection m_direction; 23 private TranslationDirection direction;
24 private Map<String, ClassMapping> m_classes; 24 private Map<String, ClassMapping> classes;
25 private TranslationIndex m_index; 25 private TranslationIndex index;
26 26
27 private ClassNameReplacer m_classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); 27 private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName();
28 28
29 public Translator() { 29 public Translator() {
30 m_direction = null; 30 this.direction = null;
31 m_classes = Maps.newHashMap(); 31 this.classes = Maps.newHashMap();
32 m_index = new TranslationIndex(); 32 this.index = new TranslationIndex();
33 } 33 }
34 34
35 public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { 35 public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) {
36 m_direction = direction; 36 this.direction = direction;
37 m_classes = classes; 37 this.classes = classes;
38 m_index = index; 38 this.index = index;
39 } 39 }
40 40
41 @SuppressWarnings("unchecked") 41 @SuppressWarnings("unchecked")
@@ -96,7 +96,7 @@ public class Translator {
96 String className = null; 96 String className = null;
97 ClassMapping classMapping = mappingsChain.get(i); 97 ClassMapping classMapping = mappingsChain.get(i);
98 if (classMapping != null) { 98 if (classMapping != null) {
99 className = m_direction.choose( 99 className = this.direction.choose(
100 classMapping.getDeobfName(), 100 classMapping.getDeobfName(),
101 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() 101 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
102 ); 102 );
@@ -114,11 +114,11 @@ public class Translator {
114 } else { 114 } else {
115 115
116 // normal classes are easy 116 // normal classes are easy
117 ClassMapping classMapping = m_classes.get(in.getName()); 117 ClassMapping classMapping = this.classes.get(in.getName());
118 if (classMapping == null) { 118 if (classMapping == null) {
119 return in; 119 return in;
120 } 120 }
121 return m_direction.choose( 121 return this.direction.choose(
122 classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, 122 classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in,
123 new ClassEntry(classMapping.getObfFullName()) 123 new ClassEntry(classMapping.getObfFullName())
124 ); 124 );
@@ -128,7 +128,7 @@ public class Translator {
128 public String translate(FieldEntry in) { 128 public String translate(FieldEntry in) {
129 129
130 // resolve the class entry 130 // resolve the class entry
131 ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); 131 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in);
132 if (resolvedClassEntry != null) { 132 if (resolvedClassEntry != null) {
133 133
134 // look for the class 134 // look for the class
@@ -136,7 +136,7 @@ public class Translator {
136 if (classMapping != null) { 136 if (classMapping != null) {
137 137
138 // look for the field 138 // look for the field
139 String translatedName = m_direction.choose( 139 String translatedName = this.direction.choose(
140 classMapping.getDeobfFieldName(in.getName(), in.getType()), 140 classMapping.getDeobfFieldName(in.getName(), in.getType()),
141 classMapping.getObfFieldName(in.getName(), translateType(in.getType())) 141 classMapping.getObfFieldName(in.getName(), translateType(in.getType()))
142 ); 142 );
@@ -159,7 +159,7 @@ public class Translator {
159 public String translate(MethodEntry in) { 159 public String translate(MethodEntry in) {
160 160
161 // resolve the class entry 161 // resolve the class entry
162 ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); 162 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in);
163 if (resolvedClassEntry != null) { 163 if (resolvedClassEntry != null) {
164 164
165 // look for class 165 // look for class
@@ -167,12 +167,12 @@ public class Translator {
167 if (classMapping != null) { 167 if (classMapping != null) {
168 168
169 // look for the method 169 // look for the method
170 MethodMapping methodMapping = m_direction.choose( 170 MethodMapping methodMapping = this.direction.choose(
171 classMapping.getMethodByObf(in.getName(), in.getSignature()), 171 classMapping.getMethodByObf(in.getName(), in.getSignature()),
172 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) 172 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))
173 ); 173 );
174 if (methodMapping != null) { 174 if (methodMapping != null) {
175 return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); 175 return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName());
176 } 176 }
177 } 177 }
178 } 178 }
@@ -211,12 +211,12 @@ public class Translator {
211 if (classMapping != null) { 211 if (classMapping != null) {
212 212
213 // look for the method 213 // look for the method
214 MethodMapping methodMapping = m_direction.choose( 214 MethodMapping methodMapping = this.direction.choose(
215 classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), 215 classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()),
216 classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) 216 classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature()))
217 ); 217 );
218 if (methodMapping != null) { 218 if (methodMapping != null) {
219 return m_direction.choose( 219 return this.direction.choose(
220 methodMapping.getDeobfArgumentName(in.getIndex()), 220 methodMapping.getDeobfArgumentName(in.getIndex()),
221 methodMapping.getObfArgumentName(in.getIndex()) 221 methodMapping.getObfArgumentName(in.getIndex())
222 ); 222 );
@@ -234,11 +234,11 @@ public class Translator {
234 } 234 }
235 235
236 public Type translateType(Type type) { 236 public Type translateType(Type type) {
237 return new Type(type, m_classNameReplacer); 237 return new Type(type, this.classNameReplacer);
238 } 238 }
239 239
240 public Signature translateSignature(Signature signature) { 240 public Signature translateSignature(Signature signature) {
241 return new Signature(signature, m_classNameReplacer); 241 return new Signature(signature, this.classNameReplacer);
242 } 242 }
243 243
244 private ClassMapping findClassMapping(ClassEntry in) { 244 private ClassMapping findClassMapping(ClassEntry in) {
@@ -253,7 +253,7 @@ public class Translator {
253 List<ClassMapping> mappingsChain = Lists.newArrayList(); 253 List<ClassMapping> mappingsChain = Lists.newArrayList();
254 254
255 // get mappings for the outer class 255 // get mappings for the outer class
256 ClassMapping outerClassMapping = m_classes.get(parts[0]); 256 ClassMapping outerClassMapping = this.classes.get(parts[0]);
257 mappingsChain.add(outerClassMapping); 257 mappingsChain.add(outerClassMapping);
258 258
259 for (int i = 1; i < parts.length; i++) { 259 for (int i = 1; i < parts.length; i++) {
@@ -261,7 +261,7 @@ public class Translator {
261 // get mappings for the inner class 261 // get mappings for the inner class
262 ClassMapping innerClassMapping = null; 262 ClassMapping innerClassMapping = null;
263 if (outerClassMapping != null) { 263 if (outerClassMapping != null) {
264 innerClassMapping = m_direction.choose( 264 innerClassMapping = this.direction.choose(
265 outerClassMapping.getInnerClassByObfSimple(parts[i]), 265 outerClassMapping.getInnerClassByObfSimple(parts[i]),
266 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) 266 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
267 ); 267 );
diff --git a/src/main/java/cuchaz/enigma/mapping/Type.java b/src/main/java/cuchaz/enigma/mapping/Type.java
index ec159415..34ddc059 100644
--- a/src/main/java/cuchaz/enigma/mapping/Type.java
+++ b/src/main/java/cuchaz/enigma/mapping/Type.java
@@ -12,12 +12,9 @@ package cuchaz.enigma.mapping;
12 12
13import com.google.common.collect.Maps; 13import com.google.common.collect.Maps;
14 14
15import java.io.Serializable;
16import java.util.Map; 15import java.util.Map;
17 16
18public class Type implements Serializable { 17public class Type {
19
20 private static final long serialVersionUID = 7862257669347104063L;
21 18
22 public enum Primitive { 19 public enum Primitive {
23 Byte('B'), 20 Byte('B'),
@@ -29,27 +26,27 @@ public class Type implements Serializable {
29 Double('D'), 26 Double('D'),
30 Boolean('Z'); 27 Boolean('Z');
31 28
32 private static final Map<Character, Primitive> m_lookup; 29 private static final Map<Character, Primitive> lookup;
33 30
34 static { 31 static {
35 m_lookup = Maps.newTreeMap(); 32 lookup = Maps.newTreeMap();
36 for (Primitive val : values()) { 33 for (Primitive val : values()) {
37 m_lookup.put(val.getCode(), val); 34 lookup.put(val.getCode(), val);
38 } 35 }
39 } 36 }
40 37
41 public static Primitive get(char code) { 38 public static Primitive get(char code) {
42 return m_lookup.get(code); 39 return lookup.get(code);
43 } 40 }
44 41
45 private char m_code; 42 private char code;
46 43
47 Primitive(char code) { 44 Primitive(char code) {
48 m_code = code; 45 this.code = code;
49 } 46 }
50 47
51 public char getCode() { 48 public char getCode() {
52 return m_code; 49 return this.code;
53 } 50 }
54 } 51 }
55 52
@@ -94,7 +91,7 @@ public class Type implements Serializable {
94 throw new IllegalArgumentException("don't know how to parse: " + in); 91 throw new IllegalArgumentException("don't know how to parse: " + in);
95 } 92 }
96 93
97 protected String m_name; 94 protected String name;
98 95
99 public Type(String name) { 96 public Type(String name) {
100 97
@@ -104,59 +101,51 @@ public class Type implements Serializable {
104 throw new IllegalArgumentException("don't use with generic types or templates: " + name); 101 throw new IllegalArgumentException("don't use with generic types or templates: " + name);
105 } 102 }
106 103
107 m_name = name; 104 this.name = name;
108 }
109
110 public Type(Type other) {
111 m_name = other.m_name;
112 }
113
114 public Type(ClassEntry classEntry) {
115 m_name = "L" + classEntry.getClassName() + ";";
116 } 105 }
117 106
118 public Type(Type other, ClassNameReplacer replacer) { 107 public Type(Type other, ClassNameReplacer replacer) {
119 m_name = other.m_name; 108 this.name = other.name;
120 if (other.isClass()) { 109 if (other.isClass()) {
121 String replacedName = replacer.replace(other.getClassEntry().getClassName()); 110 String replacedName = replacer.replace(other.getClassEntry().getClassName());
122 if (replacedName != null) { 111 if (replacedName != null) {
123 m_name = "L" + replacedName + ";"; 112 this.name = "L" + replacedName + ";";
124 } 113 }
125 } else if (other.isArray() && other.hasClass()) { 114 } else if (other.isArray() && other.hasClass()) {
126 String replacedName = replacer.replace(other.getClassEntry().getClassName()); 115 String replacedName = replacer.replace(other.getClassEntry().getClassName());
127 if (replacedName != null) { 116 if (replacedName != null) {
128 m_name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; 117 this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";";
129 } 118 }
130 } 119 }
131 } 120 }
132 121
133 @Override 122 @Override
134 public String toString() { 123 public String toString() {
135 return m_name; 124 return this.name;
136 } 125 }
137 126
138 public boolean isVoid() { 127 public boolean isVoid() {
139 return m_name.length() == 1 && m_name.charAt(0) == 'V'; 128 return this.name.length() == 1 && this.name.charAt(0) == 'V';
140 } 129 }
141 130
142 public boolean isPrimitive() { 131 public boolean isPrimitive() {
143 return m_name.length() == 1 && Primitive.get(m_name.charAt(0)) != null; 132 return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null;
144 } 133 }
145 134
146 public Primitive getPrimitive() { 135 public Primitive getPrimitive() {
147 if (!isPrimitive()) { 136 if (!isPrimitive()) {
148 throw new IllegalStateException("not a primitive"); 137 throw new IllegalStateException("not a primitive");
149 } 138 }
150 return Primitive.get(m_name.charAt(0)); 139 return Primitive.get(this.name.charAt(0));
151 } 140 }
152 141
153 public boolean isClass() { 142 public boolean isClass() {
154 return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; 143 return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';';
155 } 144 }
156 145
157 public ClassEntry getClassEntry() { 146 public ClassEntry getClassEntry() {
158 if (isClass()) { 147 if (isClass()) {
159 String name = m_name.substring(1, m_name.length() - 1); 148 String name = this.name.substring(1, this.name.length() - 1);
160 149
161 int pos = name.indexOf('<'); 150 int pos = name.indexOf('<');
162 if (pos >= 0) { 151 if (pos >= 0) {
@@ -174,21 +163,21 @@ public class Type implements Serializable {
174 } 163 }
175 164
176 public boolean isArray() { 165 public boolean isArray() {
177 return m_name.charAt(0) == '['; 166 return this.name.charAt(0) == '[';
178 } 167 }
179 168
180 public int getArrayDimension() { 169 public int getArrayDimension() {
181 if (!isArray()) { 170 if (!isArray()) {
182 throw new IllegalStateException("not an array"); 171 throw new IllegalStateException("not an array");
183 } 172 }
184 return countArrayDimension(m_name); 173 return countArrayDimension(this.name);
185 } 174 }
186 175
187 public Type getArrayType() { 176 public Type getArrayType() {
188 if (!isArray()) { 177 if (!isArray()) {
189 throw new IllegalStateException("not an array"); 178 throw new IllegalStateException("not an array");
190 } 179 }
191 return new Type(m_name.substring(getArrayDimension(), m_name.length())); 180 return new Type(this.name.substring(getArrayDimension(), this.name.length()));
192 } 181 }
193 182
194 private static String getArrayPrefix(int dimension) { 183 private static String getArrayPrefix(int dimension) {
@@ -209,16 +198,17 @@ public class Type implements Serializable {
209 } 198 }
210 199
211 public boolean equals(Type other) { 200 public boolean equals(Type other) {
212 return m_name.equals(other.m_name); 201 return this.name.equals(other.name);
213 } 202 }
214 203
215 public int hashCode() { 204 public int hashCode() {
216 return m_name.hashCode(); 205 return this.name.hashCode();
217 } 206 }
218 207
219 private static int countArrayDimension(String in) { 208 private static int countArrayDimension(String in) {
220 int i = 0; 209 int i = 0;
221 for (; i < in.length() && in.charAt(i) == '['; i++) {} 210 for (; i < in.length() && in.charAt(i) == '['; i++) {
211 }
222 return i; 212 return i;
223 } 213 }
224 214
diff --git a/src/main/java/cuchaz/enigma/mapping/IllegalNameException.java b/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java
index f2119d8d..fa21a9e5 100644
--- a/src/main/java/cuchaz/enigma/mapping/IllegalNameException.java
+++ b/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java
@@ -8,36 +8,30 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.throwables;
12 12
13public class IllegalNameException extends RuntimeException { 13public class IllegalNameException extends RuntimeException {
14 14
15 private static final long serialVersionUID = -2279910052561114323L; 15 private String name;
16 16 private String reason;
17 private String m_name;
18 private String m_reason;
19
20 public IllegalNameException(String name) {
21 this(name, null);
22 }
23 17
24 public IllegalNameException(String name, String reason) { 18 public IllegalNameException(String name, String reason) {
25 m_name = name; 19 this.name = name;
26 m_reason = reason; 20 this.reason = reason;
27 } 21 }
28 22
29 public String getReason() { 23 public String getReason() {
30 return m_reason; 24 return this.reason;
31 } 25 }
32 26
33 @Override 27 @Override
34 public String getMessage() { 28 public String getMessage() {
35 StringBuilder buf = new StringBuilder(); 29 StringBuilder buf = new StringBuilder();
36 buf.append("Illegal name: "); 30 buf.append("Illegal name: ");
37 buf.append(m_name); 31 buf.append(this.name);
38 if (m_reason != null) { 32 if (this.reason != null) {
39 buf.append(" because "); 33 buf.append(" because ");
40 buf.append(m_reason); 34 buf.append(this.reason);
41 } 35 }
42 return buf.toString(); 36 return buf.toString();
43 } 37 }
diff --git a/src/main/java/cuchaz/enigma/throwables/MappingConflict.java b/src/main/java/cuchaz/enigma/throwables/MappingConflict.java
new file mode 100644
index 00000000..5924f32a
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/throwables/MappingConflict.java
@@ -0,0 +1,7 @@
1package cuchaz.enigma.throwables;
2
3public class MappingConflict extends Exception {
4 public MappingConflict(String clazz, String name, String nameExisting) {
5 super(String.format("Conflicting mappings found for %s. The mapping file is %s and the second is %s", clazz, name, nameExisting));
6 }
7}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingParseException.java b/src/main/java/cuchaz/enigma/throwables/MappingParseException.java
index 3c25ea58..93ae2fda 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingParseException.java
+++ b/src/main/java/cuchaz/enigma/throwables/MappingParseException.java
@@ -8,12 +8,10 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.throwables;
12 12
13public class MappingParseException extends Exception { 13public class MappingParseException extends Exception {
14 14
15 private static final long serialVersionUID = -5487280332892507236L;
16
17 private int m_line; 15 private int m_line;
18 private String m_message; 16 private String m_message;
19 17
diff --git a/src/main/java/cuchaz/enigma/gui/ReadableToken.java b/src/main/java/cuchaz/enigma/utils/ReadableToken.java
index feec8c06..81193935 100644
--- a/src/main/java/cuchaz/enigma/gui/ReadableToken.java
+++ b/src/main/java/cuchaz/enigma/utils/ReadableToken.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.utils;
12 12
13public class ReadableToken { 13public class ReadableToken {
14 14
diff --git a/src/main/java/cuchaz/enigma/utils/Utils.java b/src/main/java/cuchaz/enigma/utils/Utils.java
new file mode 100644
index 00000000..e391b5ab
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/utils/Utils.java
@@ -0,0 +1,134 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.utils;
12
13import com.google.common.io.CharStreams;
14
15import java.awt.Desktop;
16import java.awt.Font;
17import java.awt.Rectangle;
18import java.awt.event.ActionEvent;
19import java.awt.event.ActionListener;
20import java.awt.event.MouseEvent;
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.InputStreamReader;
24import java.net.URI;
25import java.net.URISyntaxException;
26import java.util.Arrays;
27
28import javax.swing.*;
29import javax.swing.text.BadLocationException;
30import javax.swing.text.Highlighter;
31
32import cuchaz.enigma.analysis.Token;
33
34public class Utils {
35
36 public static int combineHashesOrdered(Object... objs) {
37 return combineHashesOrdered(Arrays.asList(objs));
38 }
39
40 public static int combineHashesOrdered(Iterable<Object> objs) {
41 final int prime = 67;
42 int result = 1;
43 for (Object obj : objs) {
44 result *= prime;
45 if (obj != null) {
46 result += obj.hashCode();
47 }
48 }
49 return result;
50 }
51
52 public static String readStreamToString(InputStream in) throws IOException {
53 return CharStreams.toString(new InputStreamReader(in, "UTF-8"));
54 }
55
56 public static String readResourceToString(String path) throws IOException {
57 InputStream in = Utils.class.getResourceAsStream(path);
58 if (in == null) {
59 throw new IllegalArgumentException("Resource not found! " + path);
60 }
61 return readStreamToString(in);
62 }
63
64 public static void openUrl(String url) {
65 if (Desktop.isDesktopSupported()) {
66 Desktop desktop = Desktop.getDesktop();
67 try {
68 desktop.browse(new URI(url));
69 } catch (IOException ex) {
70 throw new Error(ex);
71 } catch (URISyntaxException ex) {
72 throw new IllegalArgumentException(ex);
73 }
74 }
75 }
76
77 public static JLabel unboldLabel(JLabel label) {
78 Font font = label.getFont();
79 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD));
80 return label;
81 }
82
83 public static void showToolTipNow(JComponent component) {
84 // HACKHACK: trick the tooltip manager into showing the tooltip right now
85 ToolTipManager manager = ToolTipManager.sharedInstance();
86 int oldDelay = manager.getInitialDelay();
87 manager.setInitialDelay(0);
88 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false));
89 manager.setInitialDelay(oldDelay);
90 }
91
92 public static void navigateToToken(final JEditorPane editor, final Token token, final Highlighter.HighlightPainter highlightPainter) {
93
94 // set the caret position to the token
95 editor.setCaretPosition(token.start);
96 editor.grabFocus();
97
98 try {
99 // make sure the token is visible in the scroll window
100 Rectangle start = editor.modelToView(token.start);
101 Rectangle end = editor.modelToView(token.end);
102 final Rectangle show = start.union(end);
103 show.grow(start.width * 10, start.height * 6);
104 SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show));
105 } catch (BadLocationException ex) {
106 throw new Error(ex);
107 }
108
109 // highlight the token momentarily
110 final Timer timer = new Timer(200, new ActionListener() {
111 private int m_counter = 0;
112 private Object m_highlight = null;
113
114 @Override
115 public void actionPerformed(ActionEvent event) {
116 if (m_counter % 2 == 0) {
117 try {
118 m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter);
119 } catch (BadLocationException ex) {
120 // don't care
121 }
122 } else if (m_highlight != null) {
123 editor.getHighlighter().removeHighlight(m_highlight);
124 }
125
126 if (m_counter++ > 6) {
127 Timer timer = (Timer) event.getSource();
128 timer.stop();
129 }
130 }
131 });
132 timer.start();
133 }
134}