summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/Deobfuscator.java
diff options
context:
space:
mode:
authorGravatar Thog2017-03-08 08:17:04 +0100
committerGravatar Thog2017-03-08 08:17:04 +0100
commit6e464ea251cab63c776ece0b2a356f1498ffa294 (patch)
tree5ed30c03f5ac4cd2d6877874f5ede576049954f7 /src/main/java/cuchaz/enigma/Deobfuscator.java
parentDrop unix case style and implement hashCode when equals is overrided (diff)
downloadenigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.gz
enigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.xz
enigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.zip
Follow Fabric guidelines
Diffstat (limited to 'src/main/java/cuchaz/enigma/Deobfuscator.java')
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java1163
1 files changed, 580 insertions, 583 deletions
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 2602abc..934d02a 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma; 12package cuchaz.enigma;
12 13
13import com.google.common.base.Charsets; 14import com.google.common.base.Charsets;
@@ -40,587 +41,583 @@ import java.util.jar.JarOutputStream;
40 41
41public class Deobfuscator { 42public class Deobfuscator {
42 43
43 public interface ProgressListener { 44 private final JarFile jar;
44 void init(int totalWork, String title); 45 private final DecompilerSettings settings;
45 46 private final JarIndex jarIndex;
46 void onProgress(int numDone, String message); 47 private final MappingsRenamer renamer;
47 } 48 private final Map<TranslationDirection, Translator> translatorCache;
48 49 private Mappings mappings;
49 private final JarFile jar; 50 public Deobfuscator(JarFile jar) {
50 private final DecompilerSettings settings; 51 this.jar = jar;
51 private final JarIndex jarIndex; 52
52 private final MappingsRenamer renamer; 53 // build the jar index
53 private final Map<TranslationDirection, Translator> translatorCache; 54 this.jarIndex = new JarIndex();
54 private Mappings mappings; 55 this.jarIndex.indexJar(this.jar, true);
55 56
56 public Deobfuscator(JarFile jar) { 57 // config the decompiler
57 this.jar = jar; 58 this.settings = DecompilerSettings.javaDefaults();
58 59 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
59 // build the jar index 60 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
60 this.jarIndex = new JarIndex(); 61 this.settings.setForceExplicitTypeArguments(
61 this.jarIndex.indexJar(this.jar, true); 62 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
62 63 // DEBUG
63 // config the decompiler 64 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
64 this.settings = DecompilerSettings.javaDefaults(); 65 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
65 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); 66
66 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); 67 // init defaults
67 this.settings.setForceExplicitTypeArguments( 68 this.translatorCache = Maps.newTreeMap();
68 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); 69 this.renamer = new MappingsRenamer(this.jarIndex, null);
69 // DEBUG 70 // init mappings
70 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); 71 setMappings(new Mappings());
71 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); 72 }
72 73
73 // init defaults 74 public JarFile getJar() {
74 this.translatorCache = Maps.newTreeMap(); 75 return this.jar;
75 this.renamer = new MappingsRenamer(this.jarIndex, null); 76 }
76 // init mappings 77
77 setMappings(new Mappings()); 78 public String getJarName() {
78 } 79 return this.jar.getName();
79 80 }
80 public JarFile getJar() { 81
81 return this.jar; 82 public JarIndex getJarIndex() {
82 } 83 return this.jarIndex;
83 84 }
84 public String getJarName() { 85
85 return this.jar.getName(); 86 public Mappings getMappings() {
86 } 87 return this.mappings;
87 88 }
88 public JarIndex getJarIndex() { 89
89 return this.jarIndex; 90 public void setMappings(Mappings val) {
90 } 91 setMappings(val, true);
91 92 }
92 public Mappings getMappings() { 93
93 return this.mappings; 94 public void setMappings(Mappings val, boolean warnAboutDrops) {
94 } 95 if (val == null) {
95 96 val = new Mappings();
96 public void setMappings(Mappings val) { 97 }
97 setMappings(val, true); 98
98 } 99 // drop mappings that don't match the jar
99 100 MappingsChecker checker = new MappingsChecker(this.jarIndex);
100 public void setMappings(Mappings val, boolean warnAboutDrops) { 101 checker.dropBrokenMappings(val);
101 if (val == null) { 102 if (warnAboutDrops) {
102 val = new Mappings(); 103 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
103 } 104 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
104 105 }
105 // drop mappings that don't match the jar 106 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) {
106 MappingsChecker checker = new MappingsChecker(this.jarIndex); 107 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
107 checker.dropBrokenMappings(val); 108 }
108 if (warnAboutDrops) { 109 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) {
109 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { 110 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
110 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 111 }
111 } 112 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) {
112 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { 113 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
113 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 114 }
114 } 115 }
115 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { 116
116 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 117 this.mappings = val;
117 } 118 this.renamer.setMappings(mappings);
118 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { 119 this.translatorCache.clear();
119 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 120 }
120 } 121
121 } 122 public Translator getTranslator(TranslationDirection direction) {
122 123 return this.translatorCache.computeIfAbsent(direction,
123 this.mappings = val; 124 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex()));
124 this.renamer.setMappings(mappings); 125 }
125 this.translatorCache.clear(); 126
126 } 127 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) {
127 128 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) {
128 public Translator getTranslator(TranslationDirection direction) { 129 // skip inner classes
129 return this.translatorCache.computeIfAbsent(direction, 130 if (obfClassEntry.isInnerClass()) {
130 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); 131 continue;
131 } 132 }
132 133
133 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { 134 // separate the classes
134 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { 135 ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry);
135 // skip inner classes 136 if (!deobfClassEntry.equals(obfClassEntry)) {
136 if (obfClassEntry.isInnerClass()) { 137 // if the class has a mapping, clearly it's deobfuscated
137 continue; 138 deobfClasses.add(deobfClassEntry);
138 } 139 } else if (obfClassEntry.getPackageName() != null) {
139 140 // also call it deobufscated if it's not in the none package
140 // separate the classes 141 deobfClasses.add(obfClassEntry);
141 ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); 142 } else {
142 if (!deobfClassEntry.equals(obfClassEntry)) { 143 // otherwise, assume it's still obfuscated
143 // if the class has a mapping, clearly it's deobfuscated 144 obfClasses.add(obfClassEntry);
144 deobfClasses.add(deobfClassEntry); 145 }
145 } else if (obfClassEntry.getPackageName() != null) { 146 }
146 // also call it deobufscated if it's not in the none package 147 }
147 deobfClasses.add(obfClassEntry); 148
148 } else { 149 public TranslatingTypeLoader createTypeLoader() {
149 // otherwise, assume it's still obfuscated 150 return new TranslatingTypeLoader(
150 obfClasses.add(obfClassEntry); 151 this.jar,
151 } 152 this.jarIndex,
152 } 153 getTranslator(TranslationDirection.Obfuscating),
153 } 154 getTranslator(TranslationDirection.Deobfuscating)
154 155 );
155 public TranslatingTypeLoader createTypeLoader() 156 }
156 { 157
157 return new TranslatingTypeLoader( 158 public CompilationUnit getSourceTree(String className) {
158 this.jar, 159
159 this.jarIndex, 160 // we don't know if this class name is obfuscated or deobfuscated
160 getTranslator(TranslationDirection.Obfuscating), 161 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out
161 getTranslator(TranslationDirection.Deobfuscating) 162 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one
162 ); 163
163 } 164 // first, assume class name is deobf
164 165 String deobfClassName = className;
165 public CompilationUnit getSourceTree(String className) { 166
166 167 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name
167 // we don't know if this class name is obfuscated or deobfuscated 168 ClassMapping classMapping = this.mappings.getClassByObf(className);
168 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out 169 if (classMapping != null && classMapping.getDeobfName() != null) {
169 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one 170 deobfClassName = classMapping.getDeobfName();
170 171 }
171 // first, assume class name is deobf 172
172 String deobfClassName = className; 173 // set the type loader
173 174 TranslatingTypeLoader loader = createTypeLoader();
174 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name 175 this.settings.setTypeLoader(loader);
175 ClassMapping classMapping = this.mappings.getClassByObf(className); 176
176 if (classMapping != null && classMapping.getDeobfName() != null) { 177 // see if procyon can find the type
177 deobfClassName = classMapping.getDeobfName(); 178 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName);
178 } 179 if (type == null) {
179 180 throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s",
180 // set the type loader 181 className, deobfClassName, loader.getClassNamesToTry(deobfClassName)
181 TranslatingTypeLoader loader = createTypeLoader(); 182 ));
182 this.settings.setTypeLoader(loader); 183 }
183 184 TypeDefinition resolvedType = type.resolve();
184 // see if procyon can find the type 185
185 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); 186 // decompile it!
186 if (type == null) { 187 DecompilerContext context = new DecompilerContext();
187 throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", 188 context.setCurrentType(resolvedType);
188 className, deobfClassName, loader.getClassNamesToTry(deobfClassName) 189 context.setSettings(this.settings);
189 )); 190 AstBuilder builder = new AstBuilder(context);
190 } 191 builder.addType(resolvedType);
191 TypeDefinition resolvedType = type.resolve(); 192 builder.runTransformations(null);
192 193 return builder.getCompilationUnit();
193 // decompile it! 194 }
194 DecompilerContext context = new DecompilerContext(); 195
195 context.setCurrentType(resolvedType); 196 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) {
196 context.setSettings(this.settings); 197 return getSourceIndex(sourceTree, source, null);
197 AstBuilder builder = new AstBuilder(context); 198 }
198 builder.addType(resolvedType); 199
199 builder.runTransformations(null); 200 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) {
200 return builder.getCompilationUnit(); 201
201 } 202 // build the source index
202 203 SourceIndex index;
203 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { 204 if (ignoreBadTokens != null) {
204 return getSourceIndex(sourceTree, source, null); 205 index = new SourceIndex(source, ignoreBadTokens);
205 } 206 } else {
206 207 index = new SourceIndex(source);
207 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) { 208 }
208 209 sourceTree.acceptVisitor(new SourceIndexVisitor(), index);
209 // build the source index 210
210 SourceIndex index; 211 // DEBUG
211 if (ignoreBadTokens != null) { 212 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null );
212 index = new SourceIndex(source, ignoreBadTokens); 213
213 } else { 214 // resolve all the classes in the source references
214 index = new SourceIndex(source); 215 for (Token token : index.referenceTokens()) {
215 } 216 EntryReference<Entry, Entry> deobfReference = index.getDeobfReference(token);
216 sourceTree.acceptVisitor(new SourceIndexVisitor(), index); 217
217 218 // get the obfuscated entry
218 // DEBUG 219 Entry obfEntry = obfuscateEntry(deobfReference.entry);
219 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); 220
220 221 // try to resolve the class
221 // resolve all the classes in the source references 222 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry);
222 for (Token token : index.referenceTokens()) { 223 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) {
223 EntryReference<Entry, Entry> deobfReference = index.getDeobfReference(token); 224 // change the class of the entry
224 225 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry);
225 // get the obfuscated entry 226
226 Entry obfEntry = obfuscateEntry(deobfReference.entry); 227 // save the new deobfuscated reference
227 228 deobfReference.entry = deobfuscateEntry(obfEntry);
228 // try to resolve the class 229 index.replaceDeobfReference(token, deobfReference);
229 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); 230 }
230 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { 231
231 // change the class of the entry 232 // DEBUG
232 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); 233 // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) );
233 234 }
234 // save the new deobfuscated reference 235
235 deobfReference.entry = deobfuscateEntry(obfEntry); 236 return index;
236 index.replaceDeobfReference(token, deobfReference); 237 }
237 } 238
238 239 public String getSource(CompilationUnit sourceTree) {
239 // DEBUG 240 // render the AST into source
240 // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); 241 StringWriter buf = new StringWriter();
241 } 242 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null);
242 243 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null);
243 return index; 244 return buf.toString();
244 } 245 }
245 246
246 public String getSource(CompilationUnit sourceTree) { 247 public void writeSources(File dirOut, ProgressListener progress) {
247 // render the AST into source 248 // get the classes to decompile
248 StringWriter buf = new StringWriter(); 249 Set<ClassEntry> classEntries = Sets.newHashSet();
249 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); 250 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) {
250 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null); 251 // skip inner classes
251 return buf.toString(); 252 if (obfClassEntry.isInnerClass()) {
252 } 253 continue;
253 254 }
254 public void writeSources(File dirOut, ProgressListener progress) { 255
255 // get the classes to decompile 256 classEntries.add(obfClassEntry);
256 Set<ClassEntry> classEntries = Sets.newHashSet(); 257 }
257 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { 258
258 // skip inner classes 259 if (progress != null) {
259 if (obfClassEntry.isInnerClass()) { 260 progress.init(classEntries.size(), "Decompiling classes...");
260 continue; 261 }
261 } 262
262 263 // DEOBFUSCATE ALL THE THINGS!! @_@
263 classEntries.add(obfClassEntry); 264 int i = 0;
264 } 265 for (ClassEntry obfClassEntry : classEntries) {
265 266 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry));
266 if (progress != null) { 267 if (progress != null) {
267 progress.init(classEntries.size(), "Decompiling classes..."); 268 progress.onProgress(i++, deobfClassEntry.toString());
268 } 269 }
269 270
270 // DEOBFUSCATE ALL THE THINGS!! @_@ 271 try {
271 int i = 0; 272 // get the source
272 for (ClassEntry obfClassEntry : classEntries) { 273 String source = getSource(getSourceTree(obfClassEntry.getName()));
273 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); 274
274 if (progress != null) { 275 // write the file
275 progress.onProgress(i++, deobfClassEntry.toString()); 276 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java");
276 } 277 file.getParentFile().mkdirs();
277 278 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) {
278 try { 279 out.write(source);
279 // get the source 280 }
280 String source = getSource(getSourceTree(obfClassEntry.getName())); 281 } catch (Throwable t) {
281 282 // don't crash the whole world here, just log the error and keep going
282 // write the file 283 // TODO: set up logback via log4j
283 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); 284 System.err.println("Unable to deobfuscate class " + deobfClassEntry + " (" + obfClassEntry + ")");
284 file.getParentFile().mkdirs(); 285 t.printStackTrace(System.err);
285 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) { 286 }
286 out.write(source); 287 }
287 } 288 if (progress != null) {
288 } catch (Throwable t) { 289 progress.onProgress(i, "Done!");
289 // don't crash the whole world here, just log the error and keep going 290 }
290 // TODO: set up logback via log4j 291 }
291 System.err.println("Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")"); 292
292 t.printStackTrace(System.err); 293 private void addAllPotentialAncestors(Set<ClassEntry> classEntries, ClassEntry classObfEntry) {
293 } 294 for (ClassEntry interfaceEntry : jarIndex.getTranslationIndex().getInterfaces(classObfEntry)) {
294 } 295 if (classEntries.add(interfaceEntry)) {
295 if (progress != null) { 296 addAllPotentialAncestors(classEntries, interfaceEntry);
296 progress.onProgress(i, "Done!"); 297 }
297 } 298 }
298 } 299
299 300 ClassEntry superClassEntry = jarIndex.getTranslationIndex().getSuperclass(classObfEntry);
300 private void addAllPotentialAncestors(Set<ClassEntry> classEntries, ClassEntry classObfEntry) { 301 if (superClassEntry != null && classEntries.add(superClassEntry)) {
301 for (ClassEntry interfaceEntry : jarIndex.getTranslationIndex().getInterfaces(classObfEntry)) { 302 addAllPotentialAncestors(classEntries, superClassEntry);
302 if (classEntries.add(interfaceEntry)) { 303 }
303 addAllPotentialAncestors(classEntries, interfaceEntry); 304 }
304 } 305
305 } 306 private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) {
306 307 if (behaviorEntry instanceof MethodEntry) {
307 ClassEntry superClassEntry = jarIndex.getTranslationIndex().getSuperclass(classObfEntry); 308 MethodEntry methodEntry = (MethodEntry) behaviorEntry;
308 if (superClassEntry != null && classEntries.add(superClassEntry)) { 309
309 addAllPotentialAncestors(classEntries, superClassEntry); 310 Set<ClassEntry> classEntries = new HashSet<>();
310 } 311 addAllPotentialAncestors(classEntries, classObfEntry);
311 } 312
312 313 for (ClassEntry parentEntry : classEntries) {
313 private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) { 314 MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature());
314 if (behaviorEntry instanceof MethodEntry) { 315 if (jarIndex.containsObfBehavior(ancestorMethodEntry)) {
315 MethodEntry methodEntry = (MethodEntry) behaviorEntry; 316 return false;
316 317 }
317 Set<ClassEntry> classEntries = new HashSet<>(); 318 }
318 addAllPotentialAncestors(classEntries, classObfEntry); 319 }
319 320
320 for (ClassEntry parentEntry : classEntries) { 321 return true;
321 MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature()); 322 }
322 if (jarIndex.containsObfBehavior(ancestorMethodEntry)) { 323
323 return false; 324 public void rebuildMethodNames(ProgressListener progress) {
324 } 325 int i = 0;
325 } 326 Map<ClassMapping, Map<Entry, String>> renameClassMap = new HashMap<>();
326 } 327
327 328 progress.init(getMappings().classes().size() * 3, "Rebuilding method names");
328 return true; 329
329 } 330 for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) {
330 331 Map<Entry, String> renameEntries = new HashMap<>();
331 public void rebuildMethodNames(ProgressListener progress) { 332
332 int i = 0; 333 progress.onProgress(i++, classMapping.getDeobfName());
333 Map<ClassMapping, Map<Entry, String>> renameClassMap = new HashMap<>(); 334
334 335 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
335 progress.init(getMappings().classes().size() * 3, "Rebuilding method names"); 336 ClassEntry classObfEntry = classMapping.getObfEntry();
336 337 BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry);
337 for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) { 338
338 Map<Entry, String> renameEntries = new HashMap<>(); 339 if (isBehaviorProvider(classObfEntry, obfEntry)) {
339 340 if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry)
340 progress.onProgress(i++, classMapping.getDeobfName()); 341 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) {
341 342 renameEntries.put(obfEntry, methodMapping.getDeobfName());
342 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 343 }
343 ClassEntry classObfEntry = classMapping.getObfEntry(); 344
344 BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry); 345 for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) {
345 346 Entry argObfEntry = argumentMapping.getObfEntry(obfEntry);
346 if (isBehaviorProvider(classObfEntry, obfEntry)) { 347 if (hasDeobfuscatedName(argObfEntry)) {
347 if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry) 348 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName());
348 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) { 349 }
349 renameEntries.put(obfEntry, methodMapping.getDeobfName()); 350 }
350 } 351 }
351 352 }
352 for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) { 353
353 Entry argObfEntry = argumentMapping.getObfEntry(obfEntry); 354 renameClassMap.put(classMapping, renameEntries);
354 if (hasDeobfuscatedName(argObfEntry)) { 355 }
355 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName()); 356
356 } 357 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) {
357 } 358 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName());
358 } 359
359 } 360 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
360 361 Entry obfEntry = entry.getKey();
361 renameClassMap.put(classMapping, renameEntries); 362
362 } 363 removeMapping(obfEntry);
363 364 }
364 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { 365 }
365 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); 366
366 367 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) {
367 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { 368 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName());
368 Entry obfEntry = entry.getKey(); 369
369 370 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
370 removeMapping(obfEntry); 371 Entry obfEntry = entry.getKey();
371 } 372 String name = entry.getValue();
372 } 373
373 374 rename(obfEntry, name);
374 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { 375 }
375 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); 376 }
376 377 }
377 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { 378
378 Entry obfEntry = entry.getKey(); 379 public void writeJar(File out, ProgressListener progress) {
379 String name = entry.getValue(); 380 transformJar(out, progress, createTypeLoader()::transformClass);
380 381 }
381 rename(obfEntry, name); 382
382 } 383 public void protectifyJar(File out, ProgressListener progress) {
383 } 384 transformJar(out, progress, ClassProtectifier::protectify);
384 } 385 }
385 386
386 public void writeJar(File out, ProgressListener progress) { 387 public void publifyJar(File out, ProgressListener progress) {
387 transformJar(out, progress, createTypeLoader()::transformClass); 388 transformJar(out, progress, ClassPublifier::publify);
388 } 389 }
389 390
390 public void protectifyJar(File out, ProgressListener progress) { 391 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) {
391 transformJar(out, progress, ClassProtectifier::protectify); 392 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) {
392 } 393 if (progress != null) {
393 394 progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes...");
394 public void publifyJar(File out, ProgressListener progress) { 395 }
395 transformJar(out, progress, ClassPublifier::publify); 396
396 } 397 int i = 0;
397 398 for (CtClass c : JarClassIterator.classes(this.jar)) {
398 public interface ClassTransformer { 399 if (progress != null) {
399 CtClass transform(CtClass c) throws Exception; 400 progress.onProgress(i++, c.getName());
400 } 401 }
401 402
402 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { 403 try {
403 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { 404 c = transformer.transform(c);
404 if (progress != null) { 405 outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class"));
405 progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes..."); 406 outJar.write(c.toBytecode());
406 } 407 outJar.closeEntry();
407 408 } catch (Throwable t) {
408 int i = 0; 409 throw new Error("Unable to transform class " + c.getName(), t);
409 for (CtClass c : JarClassIterator.classes(this.jar)) { 410 }
410 if (progress != null) { 411 }
411 progress.onProgress(i++, c.getName()); 412 if (progress != null) {
412 } 413 progress.onProgress(i, "Done!");
413 414 }
414 try { 415
415 c = transformer.transform(c); 416 outJar.close();
416 outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); 417 } catch (IOException ex) {
417 outJar.write(c.toBytecode()); 418 throw new Error("Unable to write to Jar file!");
418 outJar.closeEntry(); 419 }
419 } catch (Throwable t) { 420 }
420 throw new Error("Unable to transform class " + c.getName(), t); 421
421 } 422 public <T extends Entry> T obfuscateEntry(T deobfEntry) {
422 } 423 if (deobfEntry == null) {
423 if (progress != null) { 424 return null;
424 progress.onProgress(i, "Done!"); 425 }
425 } 426 return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry);
426 427 }
427 outJar.close(); 428
428 } catch (IOException ex) { 429 public <T extends Entry> T deobfuscateEntry(T obfEntry) {
429 throw new Error("Unable to write to Jar file!"); 430 if (obfEntry == null) {
430 } 431 return null;
431 } 432 }
432 433 return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry);
433 public <T extends Entry> T obfuscateEntry(T deobfEntry) { 434 }
434 if (deobfEntry == null) { 435
435 return null; 436 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) {
436 } 437 if (deobfReference == null) {
437 return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); 438 return null;
438 } 439 }
439 440 return new EntryReference<>(obfuscateEntry(deobfReference.entry), obfuscateEntry(deobfReference.context), deobfReference);
440 public <T extends Entry> T deobfuscateEntry(T obfEntry) { 441 }
441 if (obfEntry == null) { 442
442 return null; 443 public <E extends Entry, C extends Entry> EntryReference<E, C> deobfuscateReference(EntryReference<E, C> obfReference) {
443 } 444 if (obfReference == null) {
444 return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); 445 return null;
445 } 446 }
446 447 return new EntryReference<>(deobfuscateEntry(obfReference.entry), deobfuscateEntry(obfReference.context), obfReference);
447 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) { 448 }
448 if (deobfReference == null) { 449
449 return null; 450 public boolean isObfuscatedIdentifier(Entry obfEntry) {
450 } 451 return isObfuscatedIdentifier(obfEntry, false);
451 return new EntryReference<>(obfuscateEntry(deobfReference.entry), obfuscateEntry(deobfReference.context), deobfReference); 452 }
452 } 453
453 454 public boolean isObfuscatedIdentifier(Entry obfEntry, boolean hack) {
454 public <E extends Entry, C extends Entry> EntryReference<E, C> deobfuscateReference(EntryReference<E, C> obfReference) { 455
455 if (obfReference == null) { 456 if (obfEntry instanceof MethodEntry) {
456 return null; 457
457 } 458 // HACKHACK: Object methods are not obfuscated identifiers
458 return new EntryReference<>(deobfuscateEntry(obfReference.entry), deobfuscateEntry(obfReference.context), obfReference); 459 MethodEntry obfMethodEntry = (MethodEntry) obfEntry;
459 } 460 String name = obfMethodEntry.getName();
460 461 String sig = obfMethodEntry.getSignature().toString();
461 public boolean isObfuscatedIdentifier(Entry obfEntry) { 462 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) {
462 return isObfuscatedIdentifier(obfEntry, false); 463 return false;
463 } 464 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) {
464 465 return false;
465 public boolean isObfuscatedIdentifier(Entry obfEntry, boolean hack) { 466 } else if (name.equals("finalize") && sig.equals("()V")) {
466 467 return false;
467 if (obfEntry instanceof MethodEntry) { 468 } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) {
468 469 return false;
469 // HACKHACK: Object methods are not obfuscated identifiers 470 } else if (name.equals("hashCode") && sig.equals("()I")) {
470 MethodEntry obfMethodEntry = (MethodEntry) obfEntry; 471 return false;
471 String name = obfMethodEntry.getName(); 472 } else if (name.equals("notify") && sig.equals("()V")) {
472 String sig = obfMethodEntry.getSignature().toString(); 473 return false;
473 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { 474 } else if (name.equals("notifyAll") && sig.equals("()V")) {
474 return false; 475 return false;
475 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) { 476 } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) {
476 return false; 477 return false;
477 } else if (name.equals("finalize") && sig.equals("()V")) { 478 } else if (name.equals("wait") && sig.equals("()V")) {
478 return false; 479 return false;
479 } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) { 480 } else if (name.equals("wait") && sig.equals("(J)V")) {
480 return false; 481 return false;
481 } else if (name.equals("hashCode") && sig.equals("()I")) { 482 } else if (name.equals("wait") && sig.equals("(JI)V")) {
482 return false; 483 return false;
483 } else if (name.equals("notify") && sig.equals("()V")) { 484 }
484 return false; 485
485 } else if (name.equals("notifyAll") && sig.equals("()V")) { 486 // FIXME: HACK EVEN MORE HACK!
486 return false; 487 if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry()))
487 } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) { 488 return true;
488 return false; 489 }
489 } else if (name.equals("wait") && sig.equals("()V")) { 490
490 return false; 491 return this.jarIndex.containsObfEntry(obfEntry);
491 } else if (name.equals("wait") && sig.equals("(J)V")) { 492 }
492 return false; 493
493 } else if (name.equals("wait") && sig.equals("(JI)V")) { 494 public boolean isRenameable(EntryReference<Entry, Entry> obfReference, boolean activeHack) {
494 return false; 495 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry(), activeHack);
495 } 496 }
496 497
497 // FIXME: HACK EVEN MORE HACK! 498 public boolean isRenameable(EntryReference<Entry, Entry> obfReference) {
498 if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry())) 499 return isRenameable(obfReference, false);
499 return true; 500 }
500 } 501
501 502 public boolean hasDeobfuscatedName(Entry obfEntry) {
502 return this.jarIndex.containsObfEntry(obfEntry); 503 Translator translator = getTranslator(TranslationDirection.Deobfuscating);
503 } 504 if (obfEntry instanceof ClassEntry) {
504 505 ClassEntry obfClass = (ClassEntry) obfEntry;
505 public boolean isRenameable(EntryReference<Entry, Entry> obfReference, boolean activeHack) { 506 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass);
506 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry(), activeHack); 507 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1);
507 } 508 return classMapping != null && classMapping.getDeobfName() != null;
508 509 } else if (obfEntry instanceof FieldEntry) {
509 public boolean isRenameable(EntryReference<Entry, Entry> obfReference) { 510 return translator.translate((FieldEntry) obfEntry) != null;
510 return isRenameable(obfReference, false); 511 } else if (obfEntry instanceof MethodEntry) {
511 } 512 return translator.translate((MethodEntry) obfEntry) != null;
512 513 } else if (obfEntry instanceof ConstructorEntry) {
513 // NOTE: these methods are a bit messy... oh well 514 // constructors have no names
514 515 return false;
515 public boolean hasDeobfuscatedName(Entry obfEntry) { 516 } else if (obfEntry instanceof ArgumentEntry) {
516 Translator translator = getTranslator(TranslationDirection.Deobfuscating); 517 return translator.translate((ArgumentEntry) obfEntry) != null;
517 if (obfEntry instanceof ClassEntry) { 518 } else if (obfEntry instanceof LocalVariableEntry) {
518 ClassEntry obfClass = (ClassEntry) obfEntry; 519 // TODO: Implement it
519 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass); 520 //return translator.translate((LocalVariableEntry)obfEntry) != null;
520 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); 521 return false;
521 return classMapping != null && classMapping.getDeobfName() != null; 522 } else {
522 } else if (obfEntry instanceof FieldEntry) { 523 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
523 return translator.translate((FieldEntry) obfEntry) != null; 524 }
524 } else if (obfEntry instanceof MethodEntry) { 525 }
525 return translator.translate((MethodEntry) obfEntry) != null; 526
526 } else if (obfEntry instanceof ConstructorEntry) { 527 public void rename(Entry obfEntry, String newName) {
527 // constructors have no names 528 rename(obfEntry, newName, true);
528 return false; 529 }
529 } else if (obfEntry instanceof ArgumentEntry) { 530
530 return translator.translate((ArgumentEntry) obfEntry) != null; 531 // NOTE: these methods are a bit messy... oh well
531 } else if (obfEntry instanceof LocalVariableEntry) { 532
532 // TODO: Implement it 533 public void rename(Entry obfEntry, String newName, boolean clearCache) {
533 //return translator.translate((LocalVariableEntry)obfEntry) != null; 534 if (obfEntry instanceof ClassEntry) {
534 return false; 535 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName));
535 } else { 536 } else if (obfEntry instanceof FieldEntry) {
536 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 537 this.renamer.setFieldName((FieldEntry) obfEntry, newName);
537 } 538 } else if (obfEntry instanceof MethodEntry) {
538 } 539 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName);
539 540 } else if (obfEntry instanceof ConstructorEntry) {
540 public void rename(Entry obfEntry, String newName) { 541 throw new IllegalArgumentException("Cannot rename constructors");
541 rename(obfEntry, newName, true); 542 } else if (obfEntry instanceof ArgumentEntry) {
542 } 543 this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName);
543 544 } else if (obfEntry instanceof LocalVariableEntry) {
544 public void rename(Entry obfEntry, String newName, boolean clearCache) { 545 // TODO: Implement it
545 if (obfEntry instanceof ClassEntry) { 546 } else {
546 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); 547 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
547 } else if (obfEntry instanceof FieldEntry) { 548 }
548 this.renamer.setFieldName((FieldEntry) obfEntry, newName); 549
549 } else if (obfEntry instanceof MethodEntry) { 550 // clear caches
550 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName); 551 if (clearCache)
551 } else if (obfEntry instanceof ConstructorEntry) { 552 this.translatorCache.clear();
552 throw new IllegalArgumentException("Cannot rename constructors"); 553 }
553 } else if (obfEntry instanceof ArgumentEntry) { 554
554 this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName); 555 public void removeMapping(Entry obfEntry) {
555 } else if (obfEntry instanceof LocalVariableEntry) { 556 if (obfEntry instanceof ClassEntry) {
556 // TODO: Implement it 557 this.renamer.removeClassMapping((ClassEntry) obfEntry);
557 } else { 558 } else if (obfEntry instanceof FieldEntry) {
558 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 559 this.renamer.removeFieldMapping((FieldEntry) obfEntry);
559 } 560 } else if (obfEntry instanceof MethodEntry) {
560 561 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry);
561 // clear caches 562 } else if (obfEntry instanceof ConstructorEntry) {
562 if (clearCache) 563 throw new IllegalArgumentException("Cannot rename constructors");
563 this.translatorCache.clear(); 564 } else if (obfEntry instanceof ArgumentEntry) {
564 } 565 this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry);
565 566 } else {
566 public void removeMapping(Entry obfEntry) { 567 throw new Error("Unknown entry type: " + obfEntry);
567 if (obfEntry instanceof ClassEntry) { 568 }
568 this.renamer.removeClassMapping((ClassEntry) obfEntry); 569
569 } else if (obfEntry instanceof FieldEntry) { 570 // clear caches
570 this.renamer.removeFieldMapping((FieldEntry) obfEntry); 571 this.translatorCache.clear();
571 } else if (obfEntry instanceof MethodEntry) { 572 }
572 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry); 573
573 } else if (obfEntry instanceof ConstructorEntry) { 574 public void markAsDeobfuscated(Entry obfEntry) {
574 throw new IllegalArgumentException("Cannot rename constructors"); 575 if (obfEntry instanceof ClassEntry) {
575 } else if (obfEntry instanceof ArgumentEntry) { 576 this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry);
576 this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry); 577 } else if (obfEntry instanceof FieldEntry) {
577 } else { 578 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry);
578 throw new Error("Unknown entry type: " + obfEntry); 579 } else if (obfEntry instanceof MethodEntry) {
579 } 580 this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry);
580 581 } else if (obfEntry instanceof ConstructorEntry) {
581 // clear caches 582 throw new IllegalArgumentException("Cannot rename constructors");
582 this.translatorCache.clear(); 583 } else if (obfEntry instanceof ArgumentEntry) {
583 } 584 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry);
584 585 } else if (obfEntry instanceof LocalVariableEntry) {
585 public void markAsDeobfuscated(Entry obfEntry) { 586 // TODO: Implement it
586 if (obfEntry instanceof ClassEntry) { 587 } else {
587 this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry); 588 throw new Error("Unknown entry type: " + obfEntry);
588 } else if (obfEntry instanceof FieldEntry) { 589 }
589 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry); 590
590 } else if (obfEntry instanceof MethodEntry) { 591 // clear caches
591 this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry); 592 this.translatorCache.clear();
592 } else if (obfEntry instanceof ConstructorEntry) { 593 }
593 throw new IllegalArgumentException("Cannot rename constructors"); 594
594 } else if (obfEntry instanceof ArgumentEntry) { 595 public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) {
595 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); 596 Entry obfEntry = obfuscateEntry(entry);
596 } else if (obfEntry instanceof LocalVariableEntry) { 597 if (obfEntry instanceof ClassEntry)
597 // TODO: Implement it 598 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry);
598 } else { 599 else if (obfEntry instanceof FieldEntry)
599 throw new Error("Unknown entry type: " + obfEntry); 600 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry);
600 } 601 else if (obfEntry instanceof BehaviorEntry)
601 602 this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry);
602 // clear caches 603 else
603 this.translatorCache.clear(); 604 throw new Error("Unknown entry type: " + obfEntry);
604 } 605 }
605 606
606 public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) 607 public Mappings.EntryModifier getModifier(Entry obEntry) {
607 { 608 Entry entry = obfuscateEntry(obEntry);
608 Entry obfEntry = obfuscateEntry(entry); 609 if (entry != null)
609 if (obfEntry instanceof ClassEntry) 610 obEntry = entry;
610 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry); 611 return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry);
611 else if (obfEntry instanceof FieldEntry) 612 }
612 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry); 613
613 else if (obfEntry instanceof BehaviorEntry) 614 public interface ProgressListener {
614 this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry); 615 void init(int totalWork, String title);
615 else 616
616 throw new Error("Unknown entry type: " + obfEntry); 617 void onProgress(int numDone, String message);
617 } 618 }
618 619
619 public Mappings.EntryModifier getModifier(Entry obEntry) 620 public interface ClassTransformer {
620 { 621 CtClass transform(CtClass c) throws Exception;
621 Entry entry = obfuscateEntry(obEntry); 622 }
622 if (entry != null)
623 obEntry = entry;
624 return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry);
625 }
626} 623}