summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/gui/GuiController.java
diff options
context:
space:
mode:
authorGravatar Michael Smith2015-05-21 23:30:00 +0100
committerGravatar Michael Smith2015-05-21 23:30:00 +0100
commite3f452250e51b7271f3989c7dfd12e4422934942 (patch)
tree5aa482f9a6e21eb318a3e23e7d8274d77c73faf6 /src/cuchaz/enigma/gui/GuiController.java
downloadenigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.tar.gz
enigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.tar.xz
enigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.zip
Support Gradle alongside SSJB
This makes builds faster, simpler and better automated but still keeps Cuchaz happy. :)
Diffstat (limited to 'src/cuchaz/enigma/gui/GuiController.java')
-rw-r--r--src/cuchaz/enigma/gui/GuiController.java358
1 files changed, 358 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java
new file mode 100644
index 0000000..6690622
--- /dev/null
+++ b/src/cuchaz/enigma/gui/GuiController.java
@@ -0,0 +1,358 @@
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 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.io.File;
14import java.io.FileReader;
15import java.io.FileWriter;
16import java.io.IOException;
17import java.util.Collection;
18import java.util.Deque;
19import java.util.List;
20import java.util.jar.JarFile;
21
22import com.google.common.collect.Lists;
23import com.google.common.collect.Queues;
24import com.strobel.decompiler.languages.java.ast.CompilationUnit;
25
26import cuchaz.enigma.Deobfuscator;
27import cuchaz.enigma.Deobfuscator.ProgressListener;
28import cuchaz.enigma.analysis.BehaviorReferenceTreeNode;
29import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
30import cuchaz.enigma.analysis.ClassInheritanceTreeNode;
31import cuchaz.enigma.analysis.EntryReference;
32import cuchaz.enigma.analysis.FieldReferenceTreeNode;
33import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
34import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
35import cuchaz.enigma.analysis.SourceIndex;
36import cuchaz.enigma.analysis.Token;
37import cuchaz.enigma.gui.ProgressDialog.ProgressRunnable;
38import cuchaz.enigma.mapping.BehaviorEntry;
39import cuchaz.enigma.mapping.ClassEntry;
40import cuchaz.enigma.mapping.Entry;
41import cuchaz.enigma.mapping.FieldEntry;
42import cuchaz.enigma.mapping.MappingParseException;
43import cuchaz.enigma.mapping.MappingsReader;
44import cuchaz.enigma.mapping.MappingsWriter;
45import cuchaz.enigma.mapping.MethodEntry;
46import cuchaz.enigma.mapping.TranslationDirection;
47
48public class GuiController {
49
50 private Deobfuscator m_deobfuscator;
51 private Gui m_gui;
52 private SourceIndex m_index;
53 private ClassEntry m_currentObfClass;
54 private boolean m_isDirty;
55 private Deque<EntryReference<Entry,Entry>> m_referenceStack;
56
57 public GuiController(Gui gui) {
58 m_gui = gui;
59 m_deobfuscator = null;
60 m_index = null;
61 m_currentObfClass = null;
62 m_isDirty = false;
63 m_referenceStack = Queues.newArrayDeque();
64 }
65
66 public boolean isDirty() {
67 return m_isDirty;
68 }
69
70 public void openJar(final JarFile jar) throws IOException {
71 m_gui.onStartOpenJar();
72 m_deobfuscator = new Deobfuscator(jar);
73 m_gui.onFinishOpenJar(m_deobfuscator.getJarName());
74 refreshClasses();
75 }
76
77 public void closeJar() {
78 m_deobfuscator = null;
79 m_gui.onCloseJar();
80 }
81
82 public void openMappings(File file) throws IOException, MappingParseException {
83 FileReader in = new FileReader(file);
84 m_deobfuscator.setMappings(new MappingsReader().read(in));
85 in.close();
86 m_isDirty = false;
87 m_gui.setMappingsFile(file);
88 refreshClasses();
89 refreshCurrentClass();
90 }
91
92 public void saveMappings(File file) throws IOException {
93 FileWriter out = new FileWriter(file);
94 new MappingsWriter().write(out, m_deobfuscator.getMappings());
95 out.close();
96 m_isDirty = false;
97 }
98
99 public void closeMappings() {
100 m_deobfuscator.setMappings(null);
101 m_gui.setMappingsFile(null);
102 refreshClasses();
103 refreshCurrentClass();
104 }
105
106 public void exportSource(final File dirOut) {
107 ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() {
108 @Override
109 public void run(ProgressListener progress) throws Exception {
110 m_deobfuscator.writeSources(dirOut, progress);
111 }
112 });
113 }
114
115 public void exportJar(final File fileOut) {
116 ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() {
117 @Override
118 public void run(ProgressListener progress) {
119 m_deobfuscator.writeJar(fileOut, progress);
120 }
121 });
122 }
123
124 public Token getToken(int pos) {
125 if (m_index == null) {
126 return null;
127 }
128 return m_index.getReferenceToken(pos);
129 }
130
131 public EntryReference<Entry,Entry> getDeobfReference(Token token) {
132 if (m_index == null) {
133 return null;
134 }
135 return m_index.getDeobfReference(token);
136 }
137
138 public ReadableToken getReadableToken(Token token) {
139 if (m_index == null) {
140 return null;
141 }
142 return new ReadableToken(
143 m_index.getLineNumber(token.start),
144 m_index.getColumnNumber(token.start),
145 m_index.getColumnNumber(token.end)
146 );
147 }
148
149 public boolean entryHasDeobfuscatedName(Entry deobfEntry) {
150 return m_deobfuscator.hasDeobfuscatedName(m_deobfuscator.obfuscateEntry(deobfEntry));
151 }
152
153 public boolean entryIsInJar(Entry deobfEntry) {
154 return m_deobfuscator.isObfuscatedIdentifier(m_deobfuscator.obfuscateEntry(deobfEntry));
155 }
156
157 public boolean referenceIsRenameable(EntryReference<Entry,Entry> deobfReference) {
158 return m_deobfuscator.isRenameable(m_deobfuscator.obfuscateReference(deobfReference));
159 }
160
161 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) {
162 ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry);
163 ClassInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getClassInheritance(
164 m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating),
165 obfClassEntry
166 );
167 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry);
168 }
169
170 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) {
171 ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry);
172 return m_deobfuscator.getJarIndex().getClassImplementations(
173 m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating),
174 obfClassEntry
175 );
176 }
177
178 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) {
179 MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry);
180 MethodInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodInheritance(
181 m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating),
182 obfMethodEntry
183 );
184 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry);
185 }
186
187 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) {
188 MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry);
189 List<MethodImplementationsTreeNode> rootNodes = m_deobfuscator.getJarIndex().getMethodImplementations(
190 m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating),
191 obfMethodEntry
192 );
193 if (rootNodes.isEmpty()) {
194 return null;
195 }
196 if (rootNodes.size() > 1) {
197 System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one.");
198 }
199 return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry);
200 }
201
202 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) {
203 FieldEntry obfFieldEntry = m_deobfuscator.obfuscateEntry(deobfFieldEntry);
204 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(
205 m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating),
206 obfFieldEntry
207 );
208 rootNode.load(m_deobfuscator.getJarIndex(), true);
209 return rootNode;
210 }
211
212 public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) {
213 BehaviorEntry obfBehaviorEntry = m_deobfuscator.obfuscateEntry(deobfBehaviorEntry);
214 BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(
215 m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating),
216 obfBehaviorEntry
217 );
218 rootNode.load(m_deobfuscator.getJarIndex(), true);
219 return rootNode;
220 }
221
222 public void rename(EntryReference<Entry,Entry> deobfReference, String newName) {
223 EntryReference<Entry,Entry> obfReference = m_deobfuscator.obfuscateReference(deobfReference);
224 m_deobfuscator.rename(obfReference.getNameableEntry(), newName);
225 m_isDirty = true;
226 refreshClasses();
227 refreshCurrentClass(obfReference);
228 }
229
230 public void removeMapping(EntryReference<Entry,Entry> deobfReference) {
231 EntryReference<Entry,Entry> obfReference = m_deobfuscator.obfuscateReference(deobfReference);
232 m_deobfuscator.removeMapping(obfReference.getNameableEntry());
233 m_isDirty = true;
234 refreshClasses();
235 refreshCurrentClass(obfReference);
236 }
237
238 public void markAsDeobfuscated(EntryReference<Entry,Entry> deobfReference) {
239 EntryReference<Entry,Entry> obfReference = m_deobfuscator.obfuscateReference(deobfReference);
240 m_deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry());
241 m_isDirty = true;
242 refreshClasses();
243 refreshCurrentClass(obfReference);
244 }
245
246 public void openDeclaration(Entry deobfEntry) {
247 if (deobfEntry == null) {
248 throw new IllegalArgumentException("Entry cannot be null!");
249 }
250 openReference(new EntryReference<Entry,Entry>(deobfEntry, deobfEntry.getName()));
251 }
252
253 public void openReference(EntryReference<Entry,Entry> deobfReference) {
254 if (deobfReference == null) {
255 throw new IllegalArgumentException("Reference cannot be null!");
256 }
257
258 // get the reference target class
259 EntryReference<Entry,Entry> obfReference = m_deobfuscator.obfuscateReference(deobfReference);
260 ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry();
261 if (!m_deobfuscator.isObfuscatedIdentifier(obfClassEntry)) {
262 throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!");
263 }
264 if (m_currentObfClass == null || !m_currentObfClass.equals(obfClassEntry)) {
265 // deobfuscate the class, then navigate to the reference
266 m_currentObfClass = obfClassEntry;
267 deobfuscate(m_currentObfClass, obfReference);
268 } else {
269 showReference(obfReference);
270 }
271 }
272
273 private void showReference(EntryReference<Entry,Entry> obfReference) {
274 EntryReference<Entry,Entry> deobfReference = m_deobfuscator.deobfuscateReference(obfReference);
275 Collection<Token> tokens = m_index.getReferenceTokens(deobfReference);
276 if (tokens.isEmpty()) {
277 // DEBUG
278 System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, m_currentObfClass));
279 } else {
280 m_gui.showTokens(tokens);
281 }
282 }
283
284 public void savePreviousReference(EntryReference<Entry,Entry> deobfReference) {
285 m_referenceStack.push(m_deobfuscator.obfuscateReference(deobfReference));
286 }
287
288 public void openPreviousReference() {
289 if (hasPreviousLocation()) {
290 openReference(m_deobfuscator.deobfuscateReference(m_referenceStack.pop()));
291 }
292 }
293
294 public boolean hasPreviousLocation() {
295 return !m_referenceStack.isEmpty();
296 }
297
298 private void refreshClasses() {
299 List<ClassEntry> obfClasses = Lists.newArrayList();
300 List<ClassEntry> deobfClasses = Lists.newArrayList();
301 m_deobfuscator.getSeparatedClasses(obfClasses, deobfClasses);
302 m_gui.setObfClasses(obfClasses);
303 m_gui.setDeobfClasses(deobfClasses);
304 }
305
306 private void refreshCurrentClass() {
307 refreshCurrentClass(null);
308 }
309
310 private void refreshCurrentClass(EntryReference<Entry,Entry> obfReference) {
311 if (m_currentObfClass != null) {
312 deobfuscate(m_currentObfClass, obfReference);
313 }
314 }
315
316 private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry,Entry> obfReference) {
317
318 m_gui.setSource("(deobfuscating...)");
319
320 // run the deobfuscator in a separate thread so we don't block the GUI event queue
321 new Thread() {
322 @Override
323 public void run() {
324 // decompile,deobfuscate the bytecode
325 CompilationUnit sourceTree = m_deobfuscator.getSourceTree(classEntry.getClassName());
326 if (sourceTree == null) {
327 // decompilation of this class is not supported
328 m_gui.setSource("Unable to find class: " + classEntry);
329 return;
330 }
331 String source = m_deobfuscator.getSource(sourceTree);
332 m_index = m_deobfuscator.getSourceIndex(sourceTree, source);
333 m_gui.setSource(m_index.getSource());
334 if (obfReference != null) {
335 showReference(obfReference);
336 }
337
338 // set the highlighted tokens
339 List<Token> obfuscatedTokens = Lists.newArrayList();
340 List<Token> deobfuscatedTokens = Lists.newArrayList();
341 List<Token> otherTokens = Lists.newArrayList();
342 for (Token token : m_index.referenceTokens()) {
343 EntryReference<Entry,Entry> reference = m_index.getDeobfReference(token);
344 if (referenceIsRenameable(reference)) {
345 if (entryHasDeobfuscatedName(reference.getNameableEntry())) {
346 deobfuscatedTokens.add(token);
347 } else {
348 obfuscatedTokens.add(token);
349 }
350 } else {
351 otherTokens.add(token);
352 }
353 }
354 m_gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens);
355 }
356 }.start();
357 }
358}