summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/EnigmaProject.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/EnigmaProject.java')
-rw-r--r--src/main/java/cuchaz/enigma/EnigmaProject.java276
1 files changed, 0 insertions, 276 deletions
diff --git a/src/main/java/cuchaz/enigma/EnigmaProject.java b/src/main/java/cuchaz/enigma/EnigmaProject.java
deleted file mode 100644
index b345fb3..0000000
--- a/src/main/java/cuchaz/enigma/EnigmaProject.java
+++ /dev/null
@@ -1,276 +0,0 @@
1package cuchaz.enigma;
2
3import com.google.common.base.Functions;
4import com.google.common.base.Preconditions;
5import cuchaz.enigma.analysis.ClassCache;
6import cuchaz.enigma.analysis.EntryReference;
7import cuchaz.enigma.analysis.index.JarIndex;
8import cuchaz.enigma.api.service.NameProposalService;
9import cuchaz.enigma.bytecode.translators.SourceFixVisitor;
10import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
11import cuchaz.enigma.network.EnigmaServer;
12import cuchaz.enigma.source.*;
13import cuchaz.enigma.translation.Translator;
14import cuchaz.enigma.translation.mapping.*;
15import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
16import cuchaz.enigma.translation.mapping.tree.EntryTree;
17import cuchaz.enigma.translation.representation.entry.ClassEntry;
18import cuchaz.enigma.translation.representation.entry.Entry;
19import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry;
21import cuchaz.enigma.utils.I18n;
22
23import cuchaz.enigma.utils.Utils;
24import org.objectweb.asm.ClassWriter;
25import org.objectweb.asm.Opcodes;
26import org.objectweb.asm.tree.ClassNode;
27
28import java.io.*;
29import java.nio.file.Files;
30import java.nio.file.Path;
31import java.util.Collection;
32import java.util.Map;
33import java.util.Objects;
34import java.util.concurrent.atomic.AtomicInteger;
35import java.util.jar.JarEntry;
36import java.util.jar.JarOutputStream;
37import java.util.stream.Collectors;
38
39public class EnigmaProject {
40 private final Enigma enigma;
41
42 private final ClassCache classCache;
43 private final JarIndex jarIndex;
44 private final byte[] jarChecksum;
45
46 private EntryRemapper mapper;
47
48 public EnigmaProject(Enigma enigma, ClassCache classCache, JarIndex jarIndex, byte[] jarChecksum) {
49 Preconditions.checkArgument(jarChecksum.length == EnigmaServer.CHECKSUM_SIZE);
50 this.enigma = enigma;
51 this.classCache = classCache;
52 this.jarIndex = jarIndex;
53 this.jarChecksum = jarChecksum;
54
55 this.mapper = EntryRemapper.empty(jarIndex);
56 }
57
58 public void setMappings(EntryTree<EntryMapping> mappings) {
59 if (mappings != null) {
60 mapper = EntryRemapper.mapped(jarIndex, mappings);
61 } else {
62 mapper = EntryRemapper.empty(jarIndex);
63 }
64 }
65
66 public Enigma getEnigma() {
67 return enigma;
68 }
69
70 public ClassCache getClassCache() {
71 return classCache;
72 }
73
74 public JarIndex getJarIndex() {
75 return jarIndex;
76 }
77
78 public byte[] getJarChecksum() {
79 return jarChecksum;
80 }
81
82 public EntryRemapper getMapper() {
83 return mapper;
84 }
85
86 public void dropMappings(ProgressListener progress) {
87 DeltaTrackingTree<EntryMapping> mappings = mapper.getObfToDeobf();
88
89 Collection<Entry<?>> dropped = dropMappings(mappings, progress);
90 for (Entry<?> entry : dropped) {
91 mappings.trackChange(entry);
92 }
93 }
94
95 private Collection<Entry<?>> dropMappings(EntryTree<EntryMapping> mappings, ProgressListener progress) {
96 // drop mappings that don't match the jar
97 MappingsChecker checker = new MappingsChecker(jarIndex, mappings);
98 MappingsChecker.Dropped dropped = checker.dropBrokenMappings(progress);
99
100 Map<Entry<?>, String> droppedMappings = dropped.getDroppedMappings();
101 for (Map.Entry<Entry<?>, String> mapping : droppedMappings.entrySet()) {
102 System.out.println("WARNING: Couldn't find " + mapping.getKey() + " (" + mapping.getValue() + ") in jar. Mapping was dropped.");
103 }
104
105 return droppedMappings.keySet();
106 }
107
108 public boolean isRenamable(Entry<?> obfEntry) {
109 if (obfEntry instanceof MethodEntry) {
110 // HACKHACK: Object methods are not obfuscated identifiers
111 MethodEntry obfMethodEntry = (MethodEntry) obfEntry;
112 String name = obfMethodEntry.getName();
113 String sig = obfMethodEntry.getDesc().toString();
114 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) {
115 return false;
116 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) {
117 return false;
118 } else if (name.equals("finalize") && sig.equals("()V")) {
119 return false;
120 } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) {
121 return false;
122 } else if (name.equals("hashCode") && sig.equals("()I")) {
123 return false;
124 } else if (name.equals("notify") && sig.equals("()V")) {
125 return false;
126 } else if (name.equals("notifyAll") && sig.equals("()V")) {
127 return false;
128 } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) {
129 return false;
130 } else if (name.equals("wait") && sig.equals("()V")) {
131 return false;
132 } else if (name.equals("wait") && sig.equals("(J)V")) {
133 return false;
134 } else if (name.equals("wait") && sig.equals("(JI)V")) {
135 return false;
136 }
137 } else if (obfEntry instanceof LocalVariableEntry && !((LocalVariableEntry) obfEntry).isArgument()) {
138 return false;
139 }
140
141 return this.jarIndex.getEntryIndex().hasEntry(obfEntry);
142 }
143
144 public boolean isRenamable(EntryReference<Entry<?>, Entry<?>> obfReference) {
145 return obfReference.isNamed() && isRenamable(obfReference.getNameableEntry());
146 }
147
148 public JarExport exportRemappedJar(ProgressListener progress) {
149 Collection<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses();
150
151 NameProposalService[] nameProposalServices = getEnigma().getServices().get(NameProposalService.TYPE).toArray(new NameProposalService[0]);
152 Translator deobfuscator = nameProposalServices.length == 0 ? mapper.getDeobfuscator() : new ProposingTranslator(mapper, nameProposalServices);
153
154 AtomicInteger count = new AtomicInteger();
155 progress.init(classEntries.size(), I18n.translate("progress.classes.deobfuscating"));
156
157 Map<String, ClassNode> compiled = classEntries.parallelStream()
158 .map(entry -> {
159 ClassEntry translatedEntry = deobfuscator.translate(entry);
160 progress.step(count.getAndIncrement(), translatedEntry.toString());
161
162 ClassNode node = classCache.getClassNode(entry.getFullName());
163 if (node != null) {
164 ClassNode translatedNode = new ClassNode();
165 node.accept(new TranslationClassVisitor(deobfuscator, Utils.ASM_VERSION, new SourceFixVisitor(Utils.ASM_VERSION, translatedNode, jarIndex)));
166 return translatedNode;
167 }
168
169 return null;
170 })
171 .filter(Objects::nonNull)
172 .collect(Collectors.toMap(n -> n.name, Functions.identity()));
173
174 return new JarExport(jarIndex, compiled);
175 }
176
177 public static final class JarExport {
178 private final JarIndex jarIndex;
179 private final Map<String, ClassNode> compiled;
180
181 JarExport(JarIndex jarIndex, Map<String, ClassNode> compiled) {
182 this.jarIndex = jarIndex;
183 this.compiled = compiled;
184 }
185
186 public void write(Path path, ProgressListener progress) throws IOException {
187 progress.init(this.compiled.size(), I18n.translate("progress.jar.writing"));
188
189 try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(path))) {
190 AtomicInteger count = new AtomicInteger();
191
192 for (ClassNode node : this.compiled.values()) {
193 progress.step(count.getAndIncrement(), node.name);
194
195 String entryName = node.name.replace('.', '/') + ".class";
196
197 ClassWriter writer = new ClassWriter(0);
198 node.accept(writer);
199
200 out.putNextEntry(new JarEntry(entryName));
201 out.write(writer.toByteArray());
202 out.closeEntry();
203 }
204 }
205 }
206
207 public SourceExport decompile(ProgressListener progress, DecompilerService decompilerService) {
208 Collection<ClassNode> classes = this.compiled.values().stream()
209 .filter(classNode -> classNode.name.indexOf('$') == -1)
210 .collect(Collectors.toList());
211
212 progress.init(classes.size(), I18n.translate("progress.classes.decompiling"));
213
214 //create a common instance outside the loop as mappings shouldn't be changing while this is happening
215 Decompiler decompiler = decompilerService.create(compiled::get, new SourceSettings(false, false));
216
217 AtomicInteger count = new AtomicInteger();
218
219 Collection<ClassSource> decompiled = classes.parallelStream()
220 .map(translatedNode -> {
221 progress.step(count.getAndIncrement(), translatedNode.name);
222
223 String source = decompileClass(translatedNode, decompiler);
224 return new ClassSource(translatedNode.name, source);
225 })
226 .collect(Collectors.toList());
227
228 return new SourceExport(decompiled);
229 }
230
231 private String decompileClass(ClassNode translatedNode, Decompiler decompiler) {
232 return decompiler.getSource(translatedNode.name).asString();
233 }
234 }
235
236 public static final class SourceExport {
237 private final Collection<ClassSource> decompiled;
238
239 SourceExport(Collection<ClassSource> decompiled) {
240 this.decompiled = decompiled;
241 }
242
243 public void write(Path path, ProgressListener progress) throws IOException {
244 progress.init(decompiled.size(), I18n.translate("progress.sources.writing"));
245
246 int count = 0;
247 for (ClassSource source : decompiled) {
248 progress.step(count++, source.name);
249
250 Path sourcePath = source.resolvePath(path);
251 source.writeTo(sourcePath);
252 }
253 }
254 }
255
256 private static class ClassSource {
257 private final String name;
258 private final String source;
259
260 ClassSource(String name, String source) {
261 this.name = name;
262 this.source = source;
263 }
264
265 void writeTo(Path path) throws IOException {
266 Files.createDirectories(path.getParent());
267 try (BufferedWriter writer = Files.newBufferedWriter(path)) {
268 writer.write(source);
269 }
270 }
271
272 Path resolvePath(Path root) {
273 return root.resolve(name.replace('.', '/') + ".java");
274 }
275 }
276}