summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorGravatar liach2019-11-08 16:35:19 -0600
committerGravatar modmuss502019-11-08 22:35:19 +0000
commitad59e46740ef636b95667615e3881fcee6fbbcb9 (patch)
tree2cc829684426f8f0373834efe4bee0513a0544f2 /src/main
parentAdd tinyv2 save/load (diff)
downloadenigma-ad59e46740ef636b95667615e3881fcee6fbbcb9.tar.gz
enigma-ad59e46740ef636b95667615e3881fcee6fbbcb9.tar.xz
enigma-ad59e46740ef636b95667615e3881fcee6fbbcb9.zip
Allow multiple services for enigma (#168)
* Allow multiple services for enigma Signed-off-by: liach <liach@users.noreply.github.com> * Delete bad dummy Signed-off-by: liach <liach@users.noreply.github.com>
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/cuchaz/enigma/Enigma.java29
-rw-r--r--src/main/java/cuchaz/enigma/EnigmaProfile.java61
-rw-r--r--src/main/java/cuchaz/enigma/EnigmaProject.java7
-rw-r--r--src/main/java/cuchaz/enigma/EnigmaServices.java14
-rw-r--r--src/main/java/cuchaz/enigma/Main.java11
-rw-r--r--src/main/java/cuchaz/enigma/ProposingTranslator.java74
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java150
-rw-r--r--src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java12
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java6
-rw-r--r--src/main/resources/META-INF/services/cuchaz.enigma.api.EnigmaPlugin1
-rw-r--r--src/main/resources/profile.json20
12 files changed, 313 insertions, 74 deletions
diff --git a/src/main/java/cuchaz/enigma/Enigma.java b/src/main/java/cuchaz/enigma/Enigma.java
index fd23b47a..4522ed72 100644
--- a/src/main/java/cuchaz/enigma/Enigma.java
+++ b/src/main/java/cuchaz/enigma/Enigma.java
@@ -12,7 +12,10 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import com.google.common.base.Preconditions; 14import com.google.common.base.Preconditions;
15import com.google.common.collect.ImmutableList;
16import com.google.common.collect.ImmutableListMultimap;
15import com.google.common.collect.ImmutableMap; 17import com.google.common.collect.ImmutableMap;
18import com.google.common.collect.ImmutableMultimap;
16import cuchaz.enigma.analysis.ClassCache; 19import cuchaz.enigma.analysis.ClassCache;
17import cuchaz.enigma.analysis.index.JarIndex; 20import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.api.EnigmaPlugin; 21import cuchaz.enigma.api.EnigmaPlugin;
@@ -24,6 +27,10 @@ import cuchaz.enigma.api.service.JarIndexerService;
24 27
25import java.io.IOException; 28import java.io.IOException;
26import java.nio.file.Path; 29import java.nio.file.Path;
30import java.util.ArrayList;
31import java.util.HashMap;
32import java.util.List;
33import java.util.Map;
27import java.util.ServiceLoader; 34import java.util.ServiceLoader;
28 35
29public class Enigma { 36public class Enigma {
@@ -47,9 +54,7 @@ public class Enigma {
47 ClassCache classCache = ClassCache.of(path); 54 ClassCache classCache = ClassCache.of(path);
48 JarIndex jarIndex = classCache.index(progress); 55 JarIndex jarIndex = classCache.index(progress);
49 56
50 services.get(JarIndexerService.TYPE).ifPresent(indexer -> { 57 services.get(JarIndexerService.TYPE).forEach(indexer -> indexer.acceptJar(classCache, jarIndex));
51 indexer.acceptJar(classCache, jarIndex);
52 });
53 58
54 return new EnigmaProject(this, classCache, jarIndex); 59 return new EnigmaProject(this, classCache, jarIndex);
55 } 60 }
@@ -95,7 +100,7 @@ public class Enigma {
95 private static class PluginContext implements EnigmaPluginContext { 100 private static class PluginContext implements EnigmaPluginContext {
96 private final EnigmaProfile profile; 101 private final EnigmaProfile profile;
97 102
98 private final ImmutableMap.Builder<EnigmaServiceType<?>, EnigmaService> services = ImmutableMap.builder(); 103 private final ImmutableListMultimap.Builder<EnigmaServiceType<?>, EnigmaService> services = ImmutableListMultimap.builder();
99 104
100 PluginContext(EnigmaProfile profile) { 105 PluginContext(EnigmaProfile profile) {
101 this.profile = profile; 106 this.profile = profile;
@@ -103,13 +108,15 @@ public class Enigma {
103 108
104 @Override 109 @Override
105 public <T extends EnigmaService> void registerService(String id, EnigmaServiceType<T> serviceType, EnigmaServiceFactory<T> factory) { 110 public <T extends EnigmaService> void registerService(String id, EnigmaServiceType<T> serviceType, EnigmaServiceFactory<T> factory) {
106 EnigmaProfile.Service serviceProfile = profile.getServiceProfile(serviceType); 111 List<EnigmaProfile.Service> serviceProfiles = profile.getServiceProfiles(serviceType);
107 112
108 // if this service type is not configured, or it is configured to use a different service id, skip 113 for (EnigmaProfile.Service serviceProfile : serviceProfiles) {
109 if (serviceProfile == null || !serviceProfile.matches(id)) return; 114 if (serviceProfile.matches(id)) {
110 115 T service = factory.create(serviceProfile::getArgument);
111 T service = factory.create(serviceProfile::getArgument); 116 services.put(serviceType, service);
112 services.put(serviceType, service); 117 break;
118 }
119 }
113 } 120 }
114 121
115 EnigmaServices buildServices() { 122 EnigmaServices buildServices() {
diff --git a/src/main/java/cuchaz/enigma/EnigmaProfile.java b/src/main/java/cuchaz/enigma/EnigmaProfile.java
index 32f31e32..5a68be14 100644
--- a/src/main/java/cuchaz/enigma/EnigmaProfile.java
+++ b/src/main/java/cuchaz/enigma/EnigmaProfile.java
@@ -2,29 +2,42 @@ package cuchaz.enigma;
2 2
3import com.google.common.collect.ImmutableMap; 3import com.google.common.collect.ImmutableMap;
4import com.google.gson.Gson; 4import com.google.gson.Gson;
5import com.google.gson.GsonBuilder;
6import com.google.gson.JsonDeserializationContext;
7import com.google.gson.JsonDeserializer;
8import com.google.gson.JsonElement;
9import com.google.gson.JsonObject;
10import com.google.gson.JsonParseException;
5import com.google.gson.annotations.SerializedName; 11import com.google.gson.annotations.SerializedName;
12import com.google.gson.reflect.TypeToken;
6import cuchaz.enigma.api.service.EnigmaServiceType; 13import cuchaz.enigma.api.service.EnigmaServiceType;
7import cuchaz.enigma.translation.mapping.MappingFileNameFormat; 14import cuchaz.enigma.translation.mapping.MappingFileNameFormat;
8import cuchaz.enigma.translation.mapping.MappingSaveParameters; 15import cuchaz.enigma.translation.mapping.MappingSaveParameters;
9 16
10import javax.annotation.Nullable;
11import java.io.Reader; 17import java.io.Reader;
18import java.lang.reflect.Type;
19import java.util.Collections;
20import java.util.List;
12import java.util.Map; 21import java.util.Map;
13import java.util.Optional; 22import java.util.Optional;
14 23
15public final class EnigmaProfile { 24public final class EnigmaProfile {
16 public static final EnigmaProfile EMPTY = new EnigmaProfile(ImmutableMap.of()); 25 public static final EnigmaProfile EMPTY = new EnigmaProfile(new ServiceContainer(ImmutableMap.of()));
17 26
18 private static final MappingSaveParameters DEFAULT_MAPPING_SAVE_PARAMETERS = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); 27 private static final MappingSaveParameters DEFAULT_MAPPING_SAVE_PARAMETERS = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF);
19 private static final Gson GSON = new Gson(); 28 private static final Gson GSON = new GsonBuilder()
29 .registerTypeAdapter(ServiceContainer.class, (JsonDeserializer<ServiceContainer>) EnigmaProfile::loadServiceContainer)
30 .create();
31 private static final Type SERVICE_LIST_TYPE = new TypeToken<List<Service>>() {
32 }.getType();
20 33
21 @SerializedName("services") 34 @SerializedName("services")
22 private final Map<String, Service> serviceProfiles; 35 private final ServiceContainer serviceProfiles;
23 36
24 @SerializedName("mapping_save_parameters") 37 @SerializedName("mapping_save_parameters")
25 private final MappingSaveParameters mappingSaveParameters = null; 38 private final MappingSaveParameters mappingSaveParameters = null;
26 39
27 private EnigmaProfile(Map<String, Service> serviceProfiles) { 40 private EnigmaProfile(ServiceContainer serviceProfiles) {
28 this.serviceProfiles = serviceProfiles; 41 this.serviceProfiles = serviceProfiles;
29 } 42 }
30 43
@@ -32,8 +45,30 @@ public final class EnigmaProfile {
32 return GSON.fromJson(reader, EnigmaProfile.class); 45 return GSON.fromJson(reader, EnigmaProfile.class);
33 } 46 }
34 47
35 @Nullable 48 private static ServiceContainer loadServiceContainer(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
36 public Service getServiceProfile(EnigmaServiceType<?> serviceType) { 49 if (!json.isJsonObject()) {
50 throw new JsonParseException("services must be an Object!");
51 }
52
53 JsonObject object = json.getAsJsonObject();
54
55 ImmutableMap.Builder<String, List<Service>> builder = ImmutableMap.builder();
56
57 for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
58 JsonElement value = entry.getValue();
59 if (value.isJsonObject()) {
60 builder.put(entry.getKey(), Collections.singletonList(GSON.fromJson(value, Service.class)));
61 } else if (value.isJsonArray()) {
62 builder.put(entry.getKey(), GSON.fromJson(value, SERVICE_LIST_TYPE));
63 } else {
64 throw new JsonParseException(String.format("Don't know how to convert %s to a list of service!", value));
65 }
66 }
67
68 return new ServiceContainer(builder.build());
69 }
70
71 public List<Service> getServiceProfiles(EnigmaServiceType<?> serviceType) {
37 return serviceProfiles.get(serviceType.key); 72 return serviceProfiles.get(serviceType.key);
38 } 73 }
39 74
@@ -59,4 +94,16 @@ public final class EnigmaProfile {
59 return args != null ? Optional.ofNullable(args.get(key)) : Optional.empty(); 94 return args != null ? Optional.ofNullable(args.get(key)) : Optional.empty();
60 } 95 }
61 } 96 }
97
98 static final class ServiceContainer {
99 private final Map<String, List<Service>> services;
100
101 ServiceContainer(Map<String, List<Service>> services) {
102 this.services = services;
103 }
104
105 List<Service> get(String key) {
106 return services.getOrDefault(key, Collections.emptyList());
107 }
108 }
62} 109}
diff --git a/src/main/java/cuchaz/enigma/EnigmaProject.java b/src/main/java/cuchaz/enigma/EnigmaProject.java
index a589c638..47a37907 100644
--- a/src/main/java/cuchaz/enigma/EnigmaProject.java
+++ b/src/main/java/cuchaz/enigma/EnigmaProject.java
@@ -144,11 +144,8 @@ public class EnigmaProject {
144 public JarExport exportRemappedJar(ProgressListener progress) { 144 public JarExport exportRemappedJar(ProgressListener progress) {
145 Collection<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses(); 145 Collection<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses();
146 146
147 Translator deobfuscator = getEnigma() 147 NameProposalService[] nameProposalServices = getEnigma().getServices().get(NameProposalService.TYPE).toArray(new NameProposalService[0]);
148 .getServices() 148 Translator deobfuscator = nameProposalServices.length == 0 ? mapper.getDeobfuscator() : new ProposingTranslator(mapper, nameProposalServices);
149 .get(NameProposalService.TYPE)
150 .map(nameProposalService -> (Translator) new ProposingTranslator(mapper, nameProposalService))
151 .orElse(mapper.getDeobfuscator());
152 149
153 AtomicInteger count = new AtomicInteger(); 150 AtomicInteger count = new AtomicInteger();
154 progress.init(classEntries.size(), "Deobfuscating classes..."); 151 progress.init(classEntries.size(), "Deobfuscating classes...");
diff --git a/src/main/java/cuchaz/enigma/EnigmaServices.java b/src/main/java/cuchaz/enigma/EnigmaServices.java
index 86507bca..45062d75 100644
--- a/src/main/java/cuchaz/enigma/EnigmaServices.java
+++ b/src/main/java/cuchaz/enigma/EnigmaServices.java
@@ -1,21 +1,21 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import com.google.common.collect.ImmutableMap; 3import com.google.common.collect.ImmutableListMultimap;
4import cuchaz.enigma.api.service.EnigmaService; 4import cuchaz.enigma.api.service.EnigmaService;
5import cuchaz.enigma.api.service.EnigmaServiceType; 5import cuchaz.enigma.api.service.EnigmaServiceType;
6 6
7import java.util.Optional; 7import java.util.Collections;
8import java.util.List;
8 9
9public final class EnigmaServices { 10public final class EnigmaServices {
10 private final ImmutableMap<EnigmaServiceType<?>, EnigmaService> services; 11 private final ImmutableListMultimap<EnigmaServiceType<?>, EnigmaService> services;
11 12
12 EnigmaServices(ImmutableMap<EnigmaServiceType<?>, EnigmaService> services) { 13 EnigmaServices(ImmutableListMultimap<EnigmaServiceType<?>, EnigmaService> services) {
13 this.services = services; 14 this.services = services;
14 } 15 }
15 16
16 @SuppressWarnings("unchecked") 17 @SuppressWarnings("unchecked")
17 public <T extends EnigmaService> Optional<T> get(EnigmaServiceType<T> type) { 18 public <T extends EnigmaService> List<T> get(EnigmaServiceType<T> type) {
18 EnigmaService service = services.get(type); 19 return (List<T>) services.get(type);
19 return Optional.ofNullable((T) service);
20 } 20 }
21} 21}
diff --git a/src/main/java/cuchaz/enigma/Main.java b/src/main/java/cuchaz/enigma/Main.java
index b7789463..dbbcee42 100644
--- a/src/main/java/cuchaz/enigma/Main.java
+++ b/src/main/java/cuchaz/enigma/Main.java
@@ -18,6 +18,8 @@ import joptsimple.*;
18 18
19import java.io.BufferedReader; 19import java.io.BufferedReader;
20import java.io.IOException; 20import java.io.IOException;
21import java.io.InputStreamReader;
22import java.nio.charset.StandardCharsets;
21import java.nio.file.Files; 23import java.nio.file.Files;
22import java.nio.file.Path; 24import java.nio.file.Path;
23import java.nio.file.Paths; 25import java.nio.file.Paths;
@@ -49,12 +51,19 @@ public class Main {
49 return; 51 return;
50 } 52 }
51 53
52 EnigmaProfile parsedProfile = EnigmaProfile.EMPTY; 54 EnigmaProfile parsedProfile;
53 if (options.has(profile)) { 55 if (options.has(profile)) {
54 Path profilePath = options.valueOf(profile); 56 Path profilePath = options.valueOf(profile);
55 try (BufferedReader reader = Files.newBufferedReader(profilePath)) { 57 try (BufferedReader reader = Files.newBufferedReader(profilePath)) {
56 parsedProfile = EnigmaProfile.parse(reader); 58 parsedProfile = EnigmaProfile.parse(reader);
57 } 59 }
60 } else {
61 try (BufferedReader reader = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream("/profile.json"), StandardCharsets.UTF_8))){
62 parsedProfile = EnigmaProfile.parse(reader);
63 } catch (IOException ex) {
64 System.out.println("Failed to load default profile, will use empty profile: " + ex.getMessage());
65 parsedProfile = EnigmaProfile.EMPTY;
66 }
58 } 67 }
59 68
60 Gui gui = new Gui(parsedProfile); 69 Gui gui = new Gui(parsedProfile);
diff --git a/src/main/java/cuchaz/enigma/ProposingTranslator.java b/src/main/java/cuchaz/enigma/ProposingTranslator.java
index 0807880e..018fbfda 100644
--- a/src/main/java/cuchaz/enigma/ProposingTranslator.java
+++ b/src/main/java/cuchaz/enigma/ProposingTranslator.java
@@ -3,41 +3,51 @@ package cuchaz.enigma;
3import cuchaz.enigma.api.service.NameProposalService; 3import cuchaz.enigma.api.service.NameProposalService;
4import cuchaz.enigma.translation.Translatable; 4import cuchaz.enigma.translation.Translatable;
5import cuchaz.enigma.translation.Translator; 5import cuchaz.enigma.translation.Translator;
6import cuchaz.enigma.translation.mapping.*; 6import cuchaz.enigma.translation.mapping.EntryRemapper;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
7import cuchaz.enigma.translation.representation.entry.Entry; 8import cuchaz.enigma.translation.representation.entry.Entry;
8 9
10import java.util.Arrays;
9import java.util.Optional; 11import java.util.Optional;
10 12
11public class ProposingTranslator implements Translator { 13public class ProposingTranslator implements Translator {
12 private final EntryRemapper mapper; 14 private final EntryRemapper mapper;
13 private final NameProposalService nameProposalService; 15 private final NameProposalService[] nameProposalServices;
14 16
15 public ProposingTranslator(EntryRemapper mapper, NameProposalService nameProposalService) { 17 public ProposingTranslator(EntryRemapper mapper, NameProposalService[] nameProposalServices) {
16 this.mapper = mapper; 18 this.mapper = mapper;
17 this.nameProposalService = nameProposalService; 19 this.nameProposalServices = nameProposalServices;
18 } 20 }
19 21
20 @Override 22 @Override
21 @SuppressWarnings("unchecked") 23 @SuppressWarnings("unchecked")
22 public <T extends Translatable> T translate(T translatable) { 24 public <T extends Translatable> T translate(T translatable) {
23 if (translatable == null) { 25 if (translatable == null) {
24 return null; 26 return null;
25 } 27 }
26 28
27 T deobfuscated = mapper.deobfuscate(translatable); 29 T deobfuscated = mapper.deobfuscate(translatable);
28 30
29 if (translatable instanceof Entry && ((Entry) deobfuscated).getName().equals(((Entry<?>) translatable).getName())) { 31 if (translatable instanceof Entry && ((Entry) deobfuscated).getName().equals(((Entry<?>) translatable).getName())) {
30 return mapper.getObfResolver() 32 return mapper.getObfResolver()
31 .resolveEntry((Entry<?>) translatable, ResolutionStrategy.RESOLVE_ROOT) 33 .resolveEntry((Entry<?>) translatable, ResolutionStrategy.RESOLVE_ROOT)
32 .stream() 34 .stream()
33 .map(e1 -> nameProposalService.proposeName(e1, mapper)) 35 .map(this::proposeName)
34 .filter(Optional::isPresent) 36 .filter(Optional::isPresent)
35 .map(Optional::get) 37 .map(Optional::get)
36 .findFirst() 38 .findFirst()
37 .map(newName -> (T) ((Entry) deobfuscated).withName(newName)) 39 .map(newName -> (T) ((Entry) deobfuscated).withName(newName))
38 .orElse(deobfuscated); 40 .orElse(deobfuscated);
39 } 41 }
40 42
41 return deobfuscated; 43 return deobfuscated;
42 } 44 }
45
46 private Optional<String> proposeName(Entry<?> entry) {
47 return Arrays.stream(nameProposalServices)
48 .map(service -> service.proposeName(entry, mapper))
49 .filter(Optional::isPresent)
50 .map(Optional::get)
51 .findFirst();
52 }
43} 53}
diff --git a/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
new file mode 100644
index 00000000..12ef709a
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
@@ -0,0 +1,150 @@
1package cuchaz.enigma.analysis;
2
3import com.strobel.core.Pair;
4import cuchaz.enigma.api.EnigmaPlugin;
5import cuchaz.enigma.api.EnigmaPluginContext;
6import cuchaz.enigma.api.service.JarIndexerService;
7import cuchaz.enigma.api.service.NameProposalService;
8import cuchaz.enigma.translation.mapping.ResolutionStrategy;
9import cuchaz.enigma.translation.representation.TypeDescriptor;
10import cuchaz.enigma.translation.representation.entry.ClassEntry;
11import cuchaz.enigma.translation.representation.entry.Entry;
12import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import cuchaz.enigma.translation.representation.entry.MethodEntry;
14import org.objectweb.asm.ClassReader;
15import org.objectweb.asm.ClassVisitor;
16import org.objectweb.asm.FieldVisitor;
17import org.objectweb.asm.MethodVisitor;
18import org.objectweb.asm.Opcodes;
19import org.objectweb.asm.tree.AbstractInsnNode;
20import org.objectweb.asm.tree.FieldInsnNode;
21import org.objectweb.asm.tree.InsnList;
22import org.objectweb.asm.tree.LdcInsnNode;
23import org.objectweb.asm.tree.MethodInsnNode;
24import org.objectweb.asm.tree.MethodNode;
25import org.objectweb.asm.tree.analysis.Analyzer;
26import org.objectweb.asm.tree.analysis.Frame;
27import org.objectweb.asm.tree.analysis.SourceInterpreter;
28import org.objectweb.asm.tree.analysis.SourceValue;
29
30import java.util.ArrayList;
31import java.util.HashMap;
32import java.util.HashSet;
33import java.util.List;
34import java.util.Map;
35import java.util.Optional;
36import java.util.Set;
37import java.util.function.UnaryOperator;
38
39public final class BuiltinPlugin implements EnigmaPlugin {
40
41 public BuiltinPlugin() {
42 }
43
44 @Override
45 public void init(EnigmaPluginContext ctx) {
46 registerEnumNamingService(ctx);
47 }
48
49 private void registerEnumNamingService(EnigmaPluginContext ctx) {
50 final Map<Entry<?>, String> names = new HashMap<>();
51 final EnumFieldNameFindingVisitor visitor = new EnumFieldNameFindingVisitor(names);
52
53 ctx.registerService("enigma:enum_initializer_indexer", JarIndexerService.TYPE, ctx1 -> (classCache, jarIndex) -> classCache.visit(() -> visitor, ClassReader.SKIP_FRAMES));
54 ctx.registerService("enigma:enum_name_proposer", NameProposalService.TYPE, ctx1 -> (obfEntry, remapper) -> Optional.ofNullable(names.get(obfEntry)));
55 }
56
57 private static final class EnumFieldNameFindingVisitor extends ClassVisitor {
58
59 private ClassEntry clazz;
60 private String className;
61 private final Map<Entry<?>, String> mappings;
62 private final Set<Pair<String, String>> enumFields = new HashSet<>();
63 private final List<MethodNode> classInits = new ArrayList<>();
64
65 EnumFieldNameFindingVisitor(Map<Entry<?>, String> mappings) {
66 super(Opcodes.ASM7);
67 this.mappings = mappings;
68 }
69
70 @Override
71 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
72 super.visit(version, access, name, signature, superName, interfaces);
73 this.className = name;
74 this.clazz = new ClassEntry(name);
75 this.enumFields.clear();
76 this.classInits.clear();
77 }
78
79 @Override
80 public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
81 if ((access & Opcodes.ACC_ENUM) != 0) {
82 if (!enumFields.add(new Pair<>(name, descriptor))) {
83 throw new IllegalArgumentException("Found two enum fields with the same name \"" + name + "\" and desc \"" + descriptor + "\"!");
84 }
85 }
86 return super.visitField(access, name, descriptor, signature, value);
87 }
88
89 @Override
90 public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
91 if ("<clinit>".equals(name)) {
92 MethodNode node = new MethodNode(api, access, name, descriptor, signature, exceptions);
93 classInits.add(node);
94 return node;
95 }
96 return super.visitMethod(access, name, descriptor, signature, exceptions);
97 }
98
99 @Override
100 public void visitEnd() {
101 super.visitEnd();
102 try {
103 collectResults();
104 } catch (Exception ex) {
105 throw new RuntimeException(ex);
106 }
107 }
108
109 private void collectResults() throws Exception {
110 String owner = className;
111 Analyzer<SourceValue> analyzer = new Analyzer<>(new SourceInterpreter());
112
113 for (MethodNode mn : classInits) {
114 Frame<SourceValue>[] frames = analyzer.analyze(className, mn);
115
116 InsnList instrs = mn.instructions;
117 for (int i = 1; i < instrs.size(); i++) {
118 AbstractInsnNode instr1 = instrs.get(i - 1);
119 AbstractInsnNode instr2 = instrs.get(i);
120 String s = null;
121
122 if (instr2.getOpcode() == Opcodes.PUTSTATIC
123 && ((FieldInsnNode) instr2).owner.equals(owner)
124 && enumFields.contains(new Pair<>(((FieldInsnNode) instr2).name, ((FieldInsnNode) instr2).desc))
125 && instr1.getOpcode() == Opcodes.INVOKESPECIAL
126 && "<init>".equals(((MethodInsnNode) instr1).name)) {
127
128 for (int j = 0; j < frames[i - 1].getStackSize(); j++) {
129 SourceValue sv = frames[i - 1].getStack(j);
130 for (AbstractInsnNode ci : sv.insns) {
131 if (ci instanceof LdcInsnNode && ((LdcInsnNode) ci).cst instanceof String) {
132 //if (s == null || !s.equals(((LdcInsnNode) ci).cst)) {
133 if (s == null) {
134 s = (String) (((LdcInsnNode) ci).cst);
135 // stringsFound++;
136 }
137 }
138 }
139 }
140 }
141
142 if (s != null) {
143 mappings.put(new FieldEntry(clazz, ((FieldInsnNode) instr2).name, new TypeDescriptor(((FieldInsnNode) instr2).desc)), s);
144 }
145 // report otherwise?
146 }
147 }
148 }
149 }
150}
diff --git a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java
index 44f70f8c..c1b163db 100644
--- a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java
+++ b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java
@@ -80,19 +80,15 @@ public class DecompiledClassSource {
80 private Optional<String> proposeName(EnigmaProject project, Entry<?> entry) { 80 private Optional<String> proposeName(EnigmaProject project, Entry<?> entry) {
81 EnigmaServices services = project.getEnigma().getServices(); 81 EnigmaServices services = project.getEnigma().getServices();
82 82
83 return services.get(NameProposalService.TYPE).flatMap(nameProposalService -> { 83 return services.get(NameProposalService.TYPE).stream().flatMap(nameProposalService -> {
84 EntryResolver resolver = project.getMapper().getObfResolver();
85
86 Collection<Entry<?>> resolved = resolver.resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT);
87 EntryRemapper mapper = project.getMapper(); 84 EntryRemapper mapper = project.getMapper();
85 Collection<Entry<?>> resolved = mapper.getObfResolver().resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT);
88 86
89 Stream<String> proposals = resolved.stream() 87 return resolved.stream()
90 .map(e -> nameProposalService.proposeName(e, mapper)) 88 .map(e -> nameProposalService.proposeName(e, mapper))
91 .filter(Optional::isPresent) 89 .filter(Optional::isPresent)
92 .map(Optional::get); 90 .map(Optional::get);
93 91 }).findFirst();
94 return proposals.findFirst();
95 });
96 } 92 }
97 93
98 @Nullable 94 @Nullable
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 52a97bbe..3ab1ceea 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -526,6 +526,8 @@ public class Gui {
526 } 526 }
527 527
528 public void onCaretMove(int pos, boolean fromClick) { 528 public void onCaretMove(int pos, boolean fromClick) {
529 if (controller.project == null)
530 return;
529 EntryRemapper mapper = controller.project.getMapper(); 531 EntryRemapper mapper = controller.project.getMapper();
530 Token token = this.controller.getToken(pos); 532 Token token = this.controller.getToken(pos);
531 boolean isToken = token != null; 533 boolean isToken = token != null;
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index 89fbd10b..54b5c92a 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -342,11 +342,11 @@ public class GuiController {
342 visibleClasses.forEach(entry -> { 342 visibleClasses.forEach(entry -> {
343 ClassEntry deobfEntry = mapper.deobfuscate(entry); 343 ClassEntry deobfEntry = mapper.deobfuscate(entry);
344 344
345 Optional<ObfuscationTestService> obfService = enigma.getServices().get(ObfuscationTestService.TYPE); 345 List<ObfuscationTestService> obfService = enigma.getServices().get(ObfuscationTestService.TYPE);
346 boolean obfuscated = deobfEntry.equals(entry); 346 boolean obfuscated = deobfEntry.equals(entry);
347 347
348 if (obfuscated && obfService.isPresent()) { 348 if (obfuscated && !obfService.isEmpty()) {
349 if (obfService.get().testDeobfuscated(entry)) { 349 if (obfService.stream().anyMatch(service -> service.testDeobfuscated(entry))) {
350 obfuscated = false; 350 obfuscated = false;
351 } 351 }
352 } 352 }
diff --git a/src/main/resources/META-INF/services/cuchaz.enigma.api.EnigmaPlugin b/src/main/resources/META-INF/services/cuchaz.enigma.api.EnigmaPlugin
new file mode 100644
index 00000000..136a3e78
--- /dev/null
+++ b/src/main/resources/META-INF/services/cuchaz.enigma.api.EnigmaPlugin
@@ -0,0 +1 @@
cuchaz.enigma.analysis.BuiltinPlugin
diff --git a/src/main/resources/profile.json b/src/main/resources/profile.json
new file mode 100644
index 00000000..e1af4cdb
--- /dev/null
+++ b/src/main/resources/profile.json
@@ -0,0 +1,20 @@
1{
2 "services": {
3 "jar_indexer": [
4 {
5 "id": "enigma:enum_initializer_indexer"
6 },
7 {
8 "id": "enigma:specialized_bridge_method_indexer"
9 }
10 ],
11 "name_proposal": [
12 {
13 "id": "enigma:enum_name_proposer"
14 },
15 {
16 "id": "enigma:specialized_method_name_proposer"
17 }
18 ]
19 }
20} \ No newline at end of file