diff options
| author | 2019-11-08 16:35:19 -0600 | |
|---|---|---|
| committer | 2019-11-08 22:35:19 +0000 | |
| commit | ad59e46740ef636b95667615e3881fcee6fbbcb9 (patch) | |
| tree | 2cc829684426f8f0373834efe4bee0513a0544f2 /src/main | |
| parent | Add tinyv2 save/load (diff) | |
| download | enigma-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.java | 29 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/EnigmaProfile.java | 61 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/EnigmaProject.java | 7 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/EnigmaServices.java | 14 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/Main.java | 11 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/ProposingTranslator.java | 74 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java | 150 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java | 12 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/gui/Gui.java | 2 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/gui/GuiController.java | 6 | ||||
| -rw-r--r-- | src/main/resources/META-INF/services/cuchaz.enigma.api.EnigmaPlugin | 1 | ||||
| -rw-r--r-- | src/main/resources/profile.json | 20 |
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 @@ | |||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import com.google.common.collect.ImmutableList; | ||
| 16 | import com.google.common.collect.ImmutableListMultimap; | ||
| 15 | import com.google.common.collect.ImmutableMap; | 17 | import com.google.common.collect.ImmutableMap; |
| 18 | import com.google.common.collect.ImmutableMultimap; | ||
| 16 | import cuchaz.enigma.analysis.ClassCache; | 19 | import cuchaz.enigma.analysis.ClassCache; |
| 17 | import cuchaz.enigma.analysis.index.JarIndex; | 20 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.api.EnigmaPlugin; | 21 | import cuchaz.enigma.api.EnigmaPlugin; |
| @@ -24,6 +27,10 @@ import cuchaz.enigma.api.service.JarIndexerService; | |||
| 24 | 27 | ||
| 25 | import java.io.IOException; | 28 | import java.io.IOException; |
| 26 | import java.nio.file.Path; | 29 | import java.nio.file.Path; |
| 30 | import java.util.ArrayList; | ||
| 31 | import java.util.HashMap; | ||
| 32 | import java.util.List; | ||
| 33 | import java.util.Map; | ||
| 27 | import java.util.ServiceLoader; | 34 | import java.util.ServiceLoader; |
| 28 | 35 | ||
| 29 | public class Enigma { | 36 | public 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 | ||
| 3 | import com.google.common.collect.ImmutableMap; | 3 | import com.google.common.collect.ImmutableMap; |
| 4 | import com.google.gson.Gson; | 4 | import com.google.gson.Gson; |
| 5 | import com.google.gson.GsonBuilder; | ||
| 6 | import com.google.gson.JsonDeserializationContext; | ||
| 7 | import com.google.gson.JsonDeserializer; | ||
| 8 | import com.google.gson.JsonElement; | ||
| 9 | import com.google.gson.JsonObject; | ||
| 10 | import com.google.gson.JsonParseException; | ||
| 5 | import com.google.gson.annotations.SerializedName; | 11 | import com.google.gson.annotations.SerializedName; |
| 12 | import com.google.gson.reflect.TypeToken; | ||
| 6 | import cuchaz.enigma.api.service.EnigmaServiceType; | 13 | import cuchaz.enigma.api.service.EnigmaServiceType; |
| 7 | import cuchaz.enigma.translation.mapping.MappingFileNameFormat; | 14 | import cuchaz.enigma.translation.mapping.MappingFileNameFormat; |
| 8 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | 15 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; |
| 9 | 16 | ||
| 10 | import javax.annotation.Nullable; | ||
| 11 | import java.io.Reader; | 17 | import java.io.Reader; |
| 18 | import java.lang.reflect.Type; | ||
| 19 | import java.util.Collections; | ||
| 20 | import java.util.List; | ||
| 12 | import java.util.Map; | 21 | import java.util.Map; |
| 13 | import java.util.Optional; | 22 | import java.util.Optional; |
| 14 | 23 | ||
| 15 | public final class EnigmaProfile { | 24 | public 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 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma; |
| 2 | 2 | ||
| 3 | import com.google.common.collect.ImmutableMap; | 3 | import com.google.common.collect.ImmutableListMultimap; |
| 4 | import cuchaz.enigma.api.service.EnigmaService; | 4 | import cuchaz.enigma.api.service.EnigmaService; |
| 5 | import cuchaz.enigma.api.service.EnigmaServiceType; | 5 | import cuchaz.enigma.api.service.EnigmaServiceType; |
| 6 | 6 | ||
| 7 | import java.util.Optional; | 7 | import java.util.Collections; |
| 8 | import java.util.List; | ||
| 8 | 9 | ||
| 9 | public final class EnigmaServices { | 10 | public 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 | ||
| 19 | import java.io.BufferedReader; | 19 | import java.io.BufferedReader; |
| 20 | import java.io.IOException; | 20 | import java.io.IOException; |
| 21 | import java.io.InputStreamReader; | ||
| 22 | import java.nio.charset.StandardCharsets; | ||
| 21 | import java.nio.file.Files; | 23 | import java.nio.file.Files; |
| 22 | import java.nio.file.Path; | 24 | import java.nio.file.Path; |
| 23 | import java.nio.file.Paths; | 25 | import 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; | |||
| 3 | import cuchaz.enigma.api.service.NameProposalService; | 3 | import cuchaz.enigma.api.service.NameProposalService; |
| 4 | import cuchaz.enigma.translation.Translatable; | 4 | import cuchaz.enigma.translation.Translatable; |
| 5 | import cuchaz.enigma.translation.Translator; | 5 | import cuchaz.enigma.translation.Translator; |
| 6 | import cuchaz.enigma.translation.mapping.*; | 6 | import cuchaz.enigma.translation.mapping.EntryRemapper; |
| 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.Entry; | 8 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 8 | 9 | ||
| 10 | import java.util.Arrays; | ||
| 9 | import java.util.Optional; | 11 | import java.util.Optional; |
| 10 | 12 | ||
| 11 | public class ProposingTranslator implements Translator { | 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.strobel.core.Pair; | ||
| 4 | import cuchaz.enigma.api.EnigmaPlugin; | ||
| 5 | import cuchaz.enigma.api.EnigmaPluginContext; | ||
| 6 | import cuchaz.enigma.api.service.JarIndexerService; | ||
| 7 | import cuchaz.enigma.api.service.NameProposalService; | ||
| 8 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 9 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 10 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 14 | import org.objectweb.asm.ClassReader; | ||
| 15 | import org.objectweb.asm.ClassVisitor; | ||
| 16 | import org.objectweb.asm.FieldVisitor; | ||
| 17 | import org.objectweb.asm.MethodVisitor; | ||
| 18 | import org.objectweb.asm.Opcodes; | ||
| 19 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 20 | import org.objectweb.asm.tree.FieldInsnNode; | ||
| 21 | import org.objectweb.asm.tree.InsnList; | ||
| 22 | import org.objectweb.asm.tree.LdcInsnNode; | ||
| 23 | import org.objectweb.asm.tree.MethodInsnNode; | ||
| 24 | import org.objectweb.asm.tree.MethodNode; | ||
| 25 | import org.objectweb.asm.tree.analysis.Analyzer; | ||
| 26 | import org.objectweb.asm.tree.analysis.Frame; | ||
| 27 | import org.objectweb.asm.tree.analysis.SourceInterpreter; | ||
| 28 | import org.objectweb.asm.tree.analysis.SourceValue; | ||
| 29 | |||
| 30 | import java.util.ArrayList; | ||
| 31 | import java.util.HashMap; | ||
| 32 | import java.util.HashSet; | ||
| 33 | import java.util.List; | ||
| 34 | import java.util.Map; | ||
| 35 | import java.util.Optional; | ||
| 36 | import java.util.Set; | ||
| 37 | import java.util.function.UnaryOperator; | ||
| 38 | |||
| 39 | public 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 | ||