diff --git a/src/main/java/org/quiltmc/enigma_plugin/proposal/FallbackProposalService.java b/src/main/java/org/quiltmc/enigma_plugin/proposal/FallbackProposalService.java index 5e4afbb..fb12f9e 100644 --- a/src/main/java/org/quiltmc/enigma_plugin/proposal/FallbackProposalService.java +++ b/src/main/java/org/quiltmc/enigma_plugin/proposal/FallbackProposalService.java @@ -9,7 +9,7 @@ public class FallbackProposalService extends NameProposerService { public FallbackProposalService(JarIndexer indexer, EnigmaServiceContext context) { super(); - this.addIfEnabled(context, Arguments.DISABLE_MOJMAP, () -> new MojmapNameProposer(context.getSingleArgument(Arguments.MOJMAP_PATH), context.getSingleArgument(Arguments.PACKAGE_NAME_OVERRIDES_PATH))); + this.addIfEnabled(context, Arguments.DISABLE_MOJMAP, () -> new MojmapNameProposer(context.getSingleArgument(Arguments.MOJMAP_PATH))); } @Override diff --git a/src/main/java/org/quiltmc/enigma_plugin/proposal/MojmapNameProposer.java b/src/main/java/org/quiltmc/enigma_plugin/proposal/MojmapNameProposer.java index e2869ad..91114fb 100644 --- a/src/main/java/org/quiltmc/enigma_plugin/proposal/MojmapNameProposer.java +++ b/src/main/java/org/quiltmc/enigma_plugin/proposal/MojmapNameProposer.java @@ -1,136 +1,51 @@ package org.quiltmc.enigma_plugin.proposal; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import org.jetbrains.annotations.Nullable; import org.quiltmc.enigma.api.Enigma; -import org.quiltmc.enigma.api.ProgressListener; -import org.quiltmc.enigma.api.analysis.index.jar.EntryIndex; import org.quiltmc.enigma.api.analysis.index.jar.JarIndex; -import org.quiltmc.enigma.api.analysis.index.mapping.MappingsIndex; -import org.quiltmc.enigma.api.analysis.index.mapping.PackageIndex; import org.quiltmc.enigma.api.translation.mapping.EntryMapping; -import org.quiltmc.enigma.api.translation.mapping.EntryRemapper; import org.quiltmc.enigma.api.translation.mapping.tree.EntryTree; import org.quiltmc.enigma.api.translation.mapping.tree.EntryTreeNode; -import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; import org.quiltmc.enigma.api.translation.representation.entry.Entry; import org.tinylog.Logger; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.Reader; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Function; public class MojmapNameProposer extends NameProposer { public static final String ID = "mojmap"; - private final String mojmapPath; - private final String packageNameOverridesPath; + private final Optional mojmapPath; // must be static for now. nasty hack to make sure we don't read mojmaps twice // we can guarantee that this is nonnull for the other proposer because jar proposal blocks dynamic proposal public static EntryTree mojmaps; - private PackageEntryList packageOverrides; @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public MojmapNameProposer(Optional mojmapPath, Optional packageNameOverridesPath) { + public MojmapNameProposer(Optional mojmapPath) { super(ID); + this.mojmapPath = mojmapPath; + } + @Override + public void insertProposedNames(Enigma enigma, JarIndex index, Map, EntryMapping> mappings) { if (mojmapPath.isPresent()) { - this.mojmapPath = mojmapPath.get(); + Path path = Path.of(mojmapPath.get()); + enigma.getReadWriteService(path).ifPresent((service) -> { + try { + mojmaps = service.read(path); + } catch (Exception e) { + Logger.error(e, "could not read mojmaps!"); + } + }); } else { Logger.error("no mojmap path provided, disabling " + this.getSourcePluginId()); - this.mojmapPath = null; } - if (packageNameOverridesPath.isPresent()) { - this.packageNameOverridesPath = packageNameOverridesPath.get(); - } else { - Logger.warn("no package name overrides path provided!"); - this.packageNameOverridesPath = null; - } - } - - @Override - public void insertProposedNames(Enigma enigma, JarIndex index, Map, EntryMapping> mappings) { - Path path = Path.of(mojmapPath); - enigma.getReadWriteService(path).ifPresent((service) -> { - try { - mojmaps = service.read(path); - } catch (Exception e) { - Logger.error(e, "could not read mojmaps!"); - } - }); - if (mojmaps != null) { - packageOverrides = readPackageJson(this.packageNameOverridesPath); mojmaps.getRootNodes().forEach((node) -> this.proposeNodeAndChildren(mappings, node)); } } - @Override - public void proposeDynamicNames(EntryRemapper remapper, Entry obfEntry, EntryMapping oldMapping, EntryMapping newMapping, Map, EntryMapping> mappings) { - if (packageOverrides != null) { - if (obfEntry == null) { - // rename all classes as per overrides - var classes = remapper.getJarIndex().getIndex(EntryIndex.class).getClasses(); - for (ClassEntry classEntry : classes) { - proposePackageName(classEntry, null, null, mappings); - } - } else if (obfEntry instanceof ClassEntry classEntry) { - // rename class - proposePackageName(classEntry, oldMapping, newMapping, mappings); - } - } - } - - private void proposePackageName(ClassEntry entry, @Nullable EntryMapping oldMapping, @Nullable EntryMapping newMapping, Map, EntryMapping> mappings) { - if (entry.isInnerClass()) { - return; - } - - EntryMapping mojmap = mojmaps.get(entry); - if (mojmap == null || mojmap.targetName() == null) { - Logger.error("no mojmap for outer class: " + entry); - return; - } - - // todo string manip is stupid here - String mojTarget = mojmap.targetName(); - String target; - String obfPackage = mojTarget.substring(0, mojTarget.lastIndexOf('/')); - - if (newMapping != null && newMapping.targetName() != null) { - target = mojTarget.substring(0, mojTarget.lastIndexOf('/')) - + newMapping.targetName().substring(newMapping.targetName().lastIndexOf('/')); - } else { - target = mojTarget; - } - - Optional optionalPackageEntry = packageOverrides.findEntry(obfPackage); - optionalPackageEntry.ifPresent(packageEntry -> { - String deobfPackageString = packageEntry.toDeobfPackageString(); - String obfPackageString = packageEntry.toObfPackageString(); - - boolean updatedName = newMapping != null && oldMapping != null && !newMapping.targetName().equals(oldMapping.targetName()); - - if (!deobfPackageString.equals(obfPackageString) || updatedName) { - String newTarget = target.replace(obfPackage + "/", deobfPackageString + "/"); - mappings.put(entry, new EntryMapping(newTarget)); - } - }); - } - private void proposeNodeAndChildren(Map, EntryMapping> mappings, EntryTreeNode node) { if (node.getValue() != null && node.getValue().targetName() != null) { this.insertProposal(mappings, node.getEntry(), node.getValue().targetName()); @@ -138,219 +53,4 @@ private void proposeNodeAndChildren(Map, EntryMapping> mappings, EntryT node.getChildNodes().forEach((child) -> proposeNodeAndChildren(mappings, child)); } - - @Nullable - public static PackageEntryList readPackageJson(Gson gson, String path) { - try { - if (path != null) { - Reader jsonReader = new FileReader(path); - PackageEntryList entries = gson.fromJson(jsonReader, PackageEntryList.class); - for (PackageEntry entry : entries) { - setupInheritanceAndValidate(entry); - } - - return entries; - } - } catch (FileNotFoundException e) { - Logger.warn("could not find old package definitions file"); - } - - return null; - } - - private static void setupInheritanceAndValidate(PackageEntry entry) { - if (entry.deobf != null && !entry.deobf.isEmpty()) { - String firstChar = String.valueOf(entry.deobf.charAt(0)); - if (firstChar.matches("[0-9]")) { - throw new InvalidOverrideException(entry, "package name cannot begin with an integer"); - } - - if (entry.deobf.contains("/")) { - throw new InvalidOverrideException(entry, "package name cannot contain a slash"); - } else if (entry.deobf.contains("-")) { - throw new InvalidOverrideException(entry, "package name cannot contain a dash"); - } else if (entry.deobf.contains(" ")) { - throw new InvalidOverrideException(entry, "package name cannot contain a space"); - } else if (!entry.deobf.toLowerCase().equals(entry.deobf)) { - throw new InvalidOverrideException(entry, "package name must be lowercase"); - } else if (!entry.deobf.matches("[a-z0-9_]+")) { - throw new InvalidOverrideException(entry, "entry must match regex '[a-z0-9_]+'"); - } - } - - for (PackageEntry child : entry.children) { - child.parent = entry; - setupInheritanceAndValidate(child); - } - } - - public static PackageEntryList readPackageJson(String path) { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - return readPackageJson(gson, path); - } - - public static PackageEntryList updatePackageJson(List oldJson, EntryTree mappings) { - PackageEntryList newJson = createPackageJson(mappings); - - for (PackageEntry rootEntry : oldJson) { - rootEntry.forEach(oldEntry -> { - if (oldEntry.deobf != null) { - PackageEntry newEntry = null; - String oldEntryString = oldEntry.toObfPackageString(); - - for (PackageEntry root : newJson) { - newEntry = root.findEntry(oldEntryString); - if (newEntry != null) { - break; - } - } - - if (newEntry != null) { - newEntry.deobf = oldEntry.deobf; - } - } - }); - } - - return newJson; - } - - public static PackageEntryList createPackageJson(EntryTree mappings) { - MappingsIndex index = new MappingsIndex(new PackageIndex()); - index.indexMappings(mappings, ProgressListener.createEmpty()); - - var packageNames = index.getIndex(PackageIndex.class).getPackageNames(); - PackageEntryList rootPackages = new PackageEntryList(); - - Map> packageNamesByDepth = new HashMap<>(); - - for (String packageName : new ArrayList<>(packageNames)) { - int slashes; - - if (!packageName.contains("/")) { - slashes = 0; - } else { - slashes = packageName.split("/").length - 1; - } - - packageNamesByDepth.computeIfAbsent(slashes, k -> new ArrayList<>()).add(packageName); - } - - for (int i = 0; i <= packageNamesByDepth.keySet().stream().mapToInt(num -> num).max().getAsInt(); i++) { - List names = packageNamesByDepth.get(i); - - if (i == 0) { - names.forEach(name -> rootPackages.add(new PackageEntry(name, ""))); - } else { - for (String name : names) { - String rootName = name.split("/")[0]; - - PackageEntry rootPackage = rootPackages.stream().filter(entry -> entry.obf.equals(rootName)).findFirst().orElse(null); - if (rootPackage != null) { - String packageUp = name.substring(0, name.lastIndexOf('/')); - - PackageEntry parent = rootPackage.findEntry(packageUp); - if (parent != null) { - String finalPackage = name.substring(name.lastIndexOf('/') + 1); - PackageEntry child = new PackageEntry(finalPackage, ""); - child.parent = parent; - parent.children.add(child); - } - } - } - } - } - - return rootPackages; - } - - public static void writePackageJson(Path path, List entries) { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - - try { - Files.write(path, gson.toJson(entries).getBytes()); - } catch (IOException e) { - Logger.error(e, "could not write updated package name overrides"); - } - } - - public static class PackageEntryList extends ArrayList { - public Optional findEntry(String obf) { - for (PackageEntry entry : this) { - var found = entry.findEntry(obf); - if (found != null) { - return Optional.of(found); - } - } - - return Optional.empty(); - } - } - - public static class PackageEntry { - public String obf; - public String deobf; - public List children; - private transient PackageEntry parent; - - public PackageEntry(String obf, String deobf) { - this.obf = obf; - this.deobf = deobf; - this.children = new ArrayList<>(); - } - - public void forEach(Consumer consumer) { - consumer.accept(this); - - for (PackageEntry child : this.children) { - consumer.accept(child); - child.forEach(consumer); - } - } - - public PackageEntry findEntry(String obf) { - if (this.equals(obf)) { - return this; - } - - for (PackageEntry entry : this.children) { - var found = entry.findEntry(obf); - if (found != null) { - return found; - } - } - - return null; - } - - public String toObfPackageString() { - return buildPackageString(entry -> entry.obf); - } - - public String toDeobfPackageString() { - return buildPackageString(entry -> entry.deobf != null && !entry.deobf.isEmpty() ? entry.deobf : entry.obf); - } - - private String buildPackageString(Function nameGetter) { - List packages = new ArrayList<>(); - PackageEntry entry = this; - while (entry != null) { - packages.add(nameGetter.apply(entry)); - entry = entry.parent; - } - - Collections.reverse(packages); - return String.join("/", packages.toArray(new String[0])); - } - - private boolean equals(String obfPackage) { - return this.toObfPackageString().equals(obfPackage); - } - } - - public static class InvalidOverrideException extends RuntimeException { - public InvalidOverrideException(PackageEntry entry, String message) { - super("Invalid package override for " + entry.obf + " (" + entry.deobf + "): " + message); - } - } } diff --git a/src/main/java/org/quiltmc/enigma_plugin/proposal/MojmapPackageProposer.java b/src/main/java/org/quiltmc/enigma_plugin/proposal/MojmapPackageProposer.java index ad9fa60..286270b 100644 --- a/src/main/java/org/quiltmc/enigma_plugin/proposal/MojmapPackageProposer.java +++ b/src/main/java/org/quiltmc/enigma_plugin/proposal/MojmapPackageProposer.java @@ -1,21 +1,46 @@ package org.quiltmc.enigma_plugin.proposal; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.jetbrains.annotations.Nullable; import org.quiltmc.enigma.api.Enigma; +import org.quiltmc.enigma.api.ProgressListener; import org.quiltmc.enigma.api.analysis.index.jar.EntryIndex; import org.quiltmc.enigma.api.analysis.index.jar.JarIndex; +import org.quiltmc.enigma.api.analysis.index.mapping.MappingsIndex; +import org.quiltmc.enigma.api.analysis.index.mapping.PackageIndex; import org.quiltmc.enigma.api.translation.mapping.EntryMapping; import org.quiltmc.enigma.api.translation.mapping.EntryRemapper; +import org.quiltmc.enigma.api.translation.mapping.tree.EntryTree; import org.quiltmc.enigma.api.translation.representation.entry.ClassEntry; import org.quiltmc.enigma.api.translation.representation.entry.Entry; import org.tinylog.Logger; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; + +import static org.quiltmc.enigma_plugin.proposal.MojmapNameProposer.mojmaps; public class MojmapPackageProposer extends NameProposer { public static final String ID = "mojmap_packages"; + private final Optional packageNameOverridesPath; + private PackageEntryList packageOverrides; - public MojmapPackageProposer() { + public MojmapPackageProposer(Optional packageNameOverridesPath) { super(ID); + this.packageNameOverridesPath = packageNameOverridesPath; } @Override @@ -25,38 +50,281 @@ public void insertProposedNames(Enigma enigma, JarIndex index, Map, Ent @Override public void proposeDynamicNames(EntryRemapper remapper, Entry obfEntry, EntryMapping oldMapping, EntryMapping newMapping, Map, EntryMapping> mappings) { - if (obfEntry == null) { - // initial proposal for all classes - for (ClassEntry classEntry : remapper.getJarIndex().getIndex(EntryIndex.class).getClasses()) { - //this.tryProposeForEntry(remapper, mappings, classEntry); + if (mojmaps != null) { + if (packageOverrides == null) { + if (packageNameOverridesPath.isPresent()) { + this.packageOverrides = readPackageJson(this.packageNameOverridesPath.get()); + } else { + Logger.warn("no package name overrides path provided!"); + this.packageOverrides = new PackageEntryList(); + } + } + + if (obfEntry == null) { + // rename all classes as per overrides + var classes = remapper.getJarIndex().getIndex(EntryIndex.class).getClasses(); + for (ClassEntry classEntry : classes) { + proposePackageName(classEntry, null, null, mappings); + } + } else if (obfEntry instanceof ClassEntry classEntry) { + // rename class + proposePackageName(classEntry, oldMapping, newMapping, mappings); } + } + } + + private void proposePackageName(ClassEntry entry, @Nullable EntryMapping oldMapping, @Nullable EntryMapping newMapping, Map, EntryMapping> mappings) { + if (entry.isInnerClass()) { + return; + } + + EntryMapping mojmap = mojmaps.get(entry); + if (mojmap == null || mojmap.targetName() == null) { + Logger.error("no mojmap for outer class: " + entry); + return; + } + + String mojTarget = mojmap.targetName(); + String target; + String obfPackage = mojTarget.substring(0, mojTarget.lastIndexOf('/')); + + if (newMapping != null && newMapping.targetName() != null) { + target = mojTarget.substring(0, mojTarget.lastIndexOf('/')) + + newMapping.targetName().substring(newMapping.targetName().lastIndexOf('/')); } else { - //this.tryProposeForEntry(remapper, mappings, obfEntry); + target = mojTarget; + } + + Optional optionalPackageEntry = packageOverrides.findEntry(obfPackage); + optionalPackageEntry.ifPresentOrElse(packageEntry -> { + String deobfPackageString = packageEntry.toDeobfPackageString(); + String obfPackageString = packageEntry.toObfPackageString(); + + boolean updatedName = newMapping != null && oldMapping != null && !newMapping.targetName().equals(oldMapping.targetName()); + + if (!deobfPackageString.equals(obfPackageString) || updatedName) { + String newTarget = target.replace(obfPackage + "/", deobfPackageString + "/"); + mappings.put(entry, new EntryMapping(newTarget)); + } + }, () -> { + boolean updatedName = newMapping != null && oldMapping != null && !newMapping.targetName().equals(oldMapping.targetName()); + if (updatedName) { + mappings.put(entry, new EntryMapping(target)); + } + }); + } + + public static PackageEntryList readPackageJson(Gson gson, @Nullable String path) { + try { + if (path != null) { + Reader jsonReader = new FileReader(path); + PackageEntryList entries = gson.fromJson(jsonReader, PackageEntryList.class); + for (PackageEntry entry : entries) { + setupInheritanceAndValidate(entry); + } + + return entries; + } + } catch (FileNotFoundException e) { + Logger.warn("could not find old package definitions file"); } + + return new PackageEntryList(); } - private void tryProposeForEntry(EntryRemapper remapper, Map, EntryMapping> mappings, Entry entry) { - var mojmaps = MojmapNameProposer.mojmaps; + private static void setupInheritanceAndValidate(PackageEntry entry) { + if (entry.deobf != null && !entry.deobf.isEmpty()) { + String firstChar = String.valueOf(entry.deobf.charAt(0)); + if (firstChar.matches("[0-9]")) { + throw new InvalidOverrideException(entry, "package name cannot begin with an integer"); + } + + if (entry.deobf.contains("/")) { + throw new InvalidOverrideException(entry, "package name cannot contain a slash"); + } else if (entry.deobf.contains("-")) { + throw new InvalidOverrideException(entry, "package name cannot contain a dash"); + } else if (entry.deobf.contains(" ")) { + throw new InvalidOverrideException(entry, "package name cannot contain a space"); + } else if (!entry.deobf.toLowerCase().equals(entry.deobf)) { + throw new InvalidOverrideException(entry, "package name must be lowercase"); + } else if (!entry.deobf.matches("[a-z0-9_]+")) { + throw new InvalidOverrideException(entry, "entry must match regex '[a-z0-9_]+'"); + } + } - if (entry instanceof ClassEntry classEntry && !classEntry.isInnerClass() && mojmaps != null) { - String newName = getMojmappedName(remapper, classEntry); - mappings.put(classEntry, new EntryMapping(newName)); + for (PackageEntry child : entry.children) { + child.parent = entry; + setupInheritanceAndValidate(child); } } - private String getMojmappedName(EntryRemapper remapper, ClassEntry classEntry) { - var mojmaps = MojmapNameProposer.mojmaps; + public static PackageEntryList readPackageJson(String path) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + return readPackageJson(gson, path); + } - var mapping = mojmaps.get(classEntry); - if (mapping != null && mapping.targetName() != null) { - String mojmapName = mapping.targetName(); - String oldPackage = mojmapName.substring(0, mojmapName.lastIndexOf('/')); - String className = remapper.deobfuscate(classEntry).getSimpleName(); + public static PackageEntryList updatePackageJson(List oldJson, EntryTree mappings) { + PackageEntryList newJson = createPackageJson(mappings); + + for (PackageEntry rootEntry : oldJson) { + rootEntry.forEach(oldEntry -> { + if (oldEntry.deobf != null) { + PackageEntry newEntry = null; + String oldEntryString = oldEntry.toObfPackageString(); + + for (PackageEntry root : newJson) { + newEntry = root.findEntry(oldEntryString); + if (newEntry != null) { + break; + } + } + + if (newEntry != null) { + newEntry.deobf = oldEntry.deobf; + } + } + }); + } + + return newJson; + } + + public static PackageEntryList createPackageJson(EntryTree mappings) { + MappingsIndex index = new MappingsIndex(new PackageIndex()); + index.indexMappings(mappings, ProgressListener.createEmpty()); + + var packageNames = index.getIndex(PackageIndex.class).getPackageNames(); + PackageEntryList rootPackages = new PackageEntryList(); + + Map> packageNamesByDepth = new HashMap<>(); + + for (String packageName : new ArrayList<>(packageNames)) { + int slashes; + + if (!packageName.contains("/")) { + slashes = 0; + } else { + slashes = packageName.split("/").length - 1; + } + + packageNamesByDepth.computeIfAbsent(slashes, k -> new ArrayList<>()).add(packageName); + } + + for (int i = 0; i <= packageNamesByDepth.keySet().stream().mapToInt(num -> num).max().getAsInt(); i++) { + List names = packageNamesByDepth.get(i); + + if (i == 0) { + names.forEach(name -> rootPackages.add(new PackageEntry(name, ""))); + } else { + for (String name : names) { + String rootName = name.split("/")[0]; + + PackageEntry rootPackage = rootPackages.stream().filter(entry -> entry.obf.equals(rootName)).findFirst().orElse(null); + if (rootPackage != null) { + String packageUp = name.substring(0, name.lastIndexOf('/')); + + PackageEntry parent = rootPackage.findEntry(packageUp); + if (parent != null) { + String finalPackage = name.substring(name.lastIndexOf('/') + 1); + PackageEntry child = new PackageEntry(finalPackage, ""); + child.parent = parent; + parent.children.add(child); + } + } + } + } + } + + return rootPackages; + } + + public static void writePackageJson(Path path, List entries) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + try { + Files.write(path, gson.toJson(entries).getBytes()); + } catch (IOException e) { + Logger.error(e, "could not write updated package name overrides"); + } + } + + public static class PackageEntryList extends ArrayList { + public Optional findEntry(String obf) { + for (PackageEntry entry : this) { + var found = entry.findEntry(obf); + if (found != null) { + return Optional.of(found); + } + } + + return Optional.empty(); + } + } + + public static class PackageEntry { + public String obf; + public String deobf; + public List children; + private transient PackageEntry parent; + + public PackageEntry(String obf, String deobf) { + this.obf = obf; + this.deobf = deobf; + this.children = new ArrayList<>(); + } + + public void forEach(Consumer consumer) { + consumer.accept(this); + + for (PackageEntry child : this.children) { + consumer.accept(child); + child.forEach(consumer); + } + } + + public PackageEntry findEntry(String obf) { + if (this.equals(obf)) { + return this; + } + + for (PackageEntry entry : this.children) { + var found = entry.findEntry(obf); + if (found != null) { + return found; + } + } - return oldPackage + "/" + className; - } else { - Logger.error("failed to propose name: could not find mojmap for " + classEntry.getFullName()); return null; } + + public String toObfPackageString() { + return buildPackageString(entry -> entry.obf); + } + + public String toDeobfPackageString() { + return buildPackageString(entry -> entry.deobf != null && !entry.deobf.isEmpty() ? entry.deobf : entry.obf); + } + + private String buildPackageString(Function nameGetter) { + List packages = new ArrayList<>(); + PackageEntry entry = this; + while (entry != null) { + packages.add(nameGetter.apply(entry)); + entry = entry.parent; + } + + Collections.reverse(packages); + return String.join("/", packages.toArray(new String[0])); + } + + private boolean equals(String obfPackage) { + return this.toObfPackageString().equals(obfPackage); + } + } + + public static class InvalidOverrideException extends RuntimeException { + public InvalidOverrideException(PackageEntry entry, String message) { + super("Invalid package override for " + entry.obf + " (" + entry.deobf + "): " + message); + } } } diff --git a/src/main/java/org/quiltmc/enigma_plugin/proposal/UncheckedProposalService.java b/src/main/java/org/quiltmc/enigma_plugin/proposal/UncheckedProposalService.java index 864d624..1156dcf 100644 --- a/src/main/java/org/quiltmc/enigma_plugin/proposal/UncheckedProposalService.java +++ b/src/main/java/org/quiltmc/enigma_plugin/proposal/UncheckedProposalService.java @@ -2,6 +2,8 @@ import org.quiltmc.enigma.api.service.EnigmaServiceContext; import org.quiltmc.enigma.api.service.NameProposalService; +import org.quiltmc.enigma.api.translation.mapping.EntryMapping; +import org.quiltmc.enigma.api.translation.representation.entry.Entry; import org.quiltmc.enigma_plugin.Arguments; import org.quiltmc.enigma_plugin.QuiltEnigmaPlugin; import org.quiltmc.enigma_plugin.index.JarIndexer; @@ -9,7 +11,7 @@ public class UncheckedProposalService extends NameProposerService { public UncheckedProposalService(JarIndexer indexer, EnigmaServiceContext context) { super(); - this.addIfEnabled(context, Arguments.DISABLE_MOJMAP, MojmapPackageProposer::new); + this.addIfEnabled(context, Arguments.DISABLE_MOJMAP, () -> new MojmapPackageProposer(context.getSingleArgument(Arguments.PACKAGE_NAME_OVERRIDES_PATH))); } @Override @@ -18,7 +20,7 @@ public String getId() { } @Override - public boolean bypassValidation() { - return true; + public void validateProposedMapping(Entry entry, EntryMapping mapping) { + // no-op } } diff --git a/src/test/java/org/quiltmc/enigma_plugin/MojmapTest.java b/src/test/java/org/quiltmc/enigma_plugin/MojmapTest.java index 6a2ac58..6ed573a 100644 --- a/src/test/java/org/quiltmc/enigma_plugin/MojmapTest.java +++ b/src/test/java/org/quiltmc/enigma_plugin/MojmapTest.java @@ -20,6 +20,7 @@ import org.quiltmc.enigma.util.validation.PrintNotifier; import org.quiltmc.enigma.util.validation.ValidationContext; import org.quiltmc.enigma_plugin.proposal.MojmapNameProposer; +import org.quiltmc.enigma_plugin.proposal.MojmapPackageProposer; import org.quiltmc.launchermeta.version.v1.DownloadableFile; import org.quiltmc.launchermeta.version.v1.Version; import org.quiltmc.launchermeta.version_manifest.VersionEntry; @@ -115,20 +116,22 @@ private static void download(String url, File file) throws IOException { void setupEnigma(Path jarPath, Path mojmapPath, Path overridesPath) throws IOException { String profileString = """ { - "services": { - "name_proposal": [ - { - "id": "quiltmc:name_proposal/fallback", - "args": { - "mojmap_path": "{MOJMAP_PATH}", - "package_name_overrides_path": "{OVERRIDES_PATH}" - } - }, - { - "id": "quiltmc:name_proposal/unchecked" + "services": { + "name_proposal": [ + { + "id": "quiltmc:name_proposal/fallback", + "args": { + "mojmap_path": "{MOJMAP_PATH}" } - ] - } + }, + { + "id": "quiltmc:name_proposal/unchecked", + "args": { + "package_name_overrides_path": "{OVERRIDES_PATH}" + } + } + ] + } } """; @@ -182,8 +185,8 @@ void testOverrideGeneration() throws IOException, MappingParseException { Path mappingPath = Path.of("./src/test/resources/mojmap_test/example_mappings.mapping"); var mappings = project.getEnigma().readMappings(mappingPath).get(); - var entries = MojmapNameProposer.createPackageJson(mappings); - MojmapNameProposer.writePackageJson(tempFile, entries); + var entries = MojmapPackageProposer.createPackageJson(mappings); + MojmapPackageProposer.writePackageJson(tempFile, entries); String expected = Files.readString(Path.of("./src/test/resources/mojmap_test/example_mappings_empty.json")); String actual = Files.readString(tempFile); @@ -197,8 +200,8 @@ void testPackageNameOverrideGenerationMojmap() throws IOException { Path tempFile = Files.createTempFile("temp_package_overrides_moj", "json"); - var entries = MojmapNameProposer.createPackageJson(MojmapNameProposer.mojmaps); - MojmapNameProposer.writePackageJson(tempFile, entries); + var entries = MojmapPackageProposer.createPackageJson(MojmapNameProposer.mojmaps); + MojmapPackageProposer.writePackageJson(tempFile, entries); String expected = Files.readString(Path.of("./src/test/resources/mojmap_test/expected.json")); String actual = Files.readString(tempFile); @@ -216,9 +219,9 @@ void testOverrideUpdating() throws IOException, MappingParseException { var mappings = project.getEnigma().readMappings(mappingPath).get(); - var oldPackageJson = MojmapNameProposer.readPackageJson(oldOverrides.toString()); - var updated = MojmapNameProposer.updatePackageJson(oldPackageJson, mappings); - MojmapNameProposer.writePackageJson(tempFile, updated); + var oldPackageJson = MojmapPackageProposer.readPackageJson(oldOverrides.toString()); + var updated = MojmapPackageProposer.updatePackageJson(oldPackageJson, mappings); + MojmapPackageProposer.writePackageJson(tempFile, updated); String expected = Files.readString(newOverrides); String actual = Files.readString(tempFile); @@ -275,16 +278,22 @@ void testDynamicOverrideRenaming() throws IOException, MappingParseException { @Test void testInvalidOverrides() { - assertThrows(MojmapNameProposer.InvalidOverrideException.class, () -> setupEnigma(overrideRenamingJar, overrideRenamingMappings, beginWithInt)); - assertThrows(MojmapNameProposer.InvalidOverrideException.class, () -> setupEnigma(overrideRenamingJar, overrideRenamingMappings, hyphens)); - assertThrows(MojmapNameProposer.InvalidOverrideException.class, () -> setupEnigma(overrideRenamingJar, overrideRenamingMappings, multipleInvalidOverrides)); - assertThrows(MojmapNameProposer.InvalidOverrideException.class, () -> setupEnigma(overrideRenamingJar, overrideRenamingMappings, slashes)); - assertThrows(MojmapNameProposer.InvalidOverrideException.class, () -> setupEnigma(overrideRenamingJar, overrideRenamingMappings, spaces)); - assertThrows(MojmapNameProposer.InvalidOverrideException.class, () -> setupEnigma(overrideRenamingJar, overrideRenamingMappings, uppercases)); + assertThrows(MojmapPackageProposer.InvalidOverrideException.class, () -> setupForOverrideValidation(beginWithInt)); + assertThrows(MojmapPackageProposer.InvalidOverrideException.class, () -> setupForOverrideValidation(hyphens)); + assertThrows(MojmapPackageProposer.InvalidOverrideException.class, () -> setupForOverrideValidation(multipleInvalidOverrides)); + assertThrows(MojmapPackageProposer.InvalidOverrideException.class, () -> setupForOverrideValidation(slashes)); + assertThrows(MojmapPackageProposer.InvalidOverrideException.class, () -> setupForOverrideValidation(spaces)); + assertThrows(MojmapPackageProposer.InvalidOverrideException.class, () -> setupForOverrideValidation(uppercases)); assertDoesNotThrow(() -> setupEnigma(overrideRenamingJar, overrideRenamingMappings, validOverrides)); } + void setupForOverrideValidation(Path overridesPath) throws MappingParseException, IOException { + setupEnigma(MojmapTest.overrideRenamingJar, MojmapTest.overrideRenamingMappings, overridesPath); + // manually trigger dynamic proposal so validation is run + project.setMappings(project.getEnigma().readMappings(MojmapTest.overrideRenamingMappings).get(), ProgressListener.createEmpty()); + } + private void assertMapping(Entry entry, String name, TokenType type) { var mapping = project.getRemapper().getMapping(entry); assertEquals(entry, name, mapping.targetName()); diff --git a/src/test/resources/mojmap_test/update_test/example_mappings.json b/src/test/resources/mojmap_test/update_test/example_mappings.json index e747142..d93a77d 100644 --- a/src/test/resources/mojmap_test/update_test/example_mappings.json +++ b/src/test/resources/mojmap_test/update_test/example_mappings.json @@ -32,7 +32,7 @@ "children": [ { "obf": "a", - "deobf": "listen to the 2016 album aura obelisk by octagrape it goes crazy", + "deobf": "listen_to_the_2016_album_aura_obelisk_by_octagrape_it_goes_crazy", "children": [] } ] diff --git a/src/test/resources/mojmap_test/update_test/example_mappings_expected.json b/src/test/resources/mojmap_test/update_test/example_mappings_expected.json index 90e3761..3f04e8f 100644 --- a/src/test/resources/mojmap_test/update_test/example_mappings_expected.json +++ b/src/test/resources/mojmap_test/update_test/example_mappings_expected.json @@ -44,7 +44,7 @@ "children": [ { "obf": "a", - "deobf": "listen to the 2016 album aura obelisk by octagrape it goes crazy", + "deobf": "listen_to_the_2016_album_aura_obelisk_by_octagrape_it_goes_crazy", "children": [] } ]