From 0f47403d0220757fed189b76e2071e25b1025cb8 Mon Sep 17 00:00:00 2001 From: Runemoro Date: Wed, 3 Jun 2020 13:39:42 -0400 Subject: Split GUI code to separate module (#242) * Split into modules * Post merge compile fixes Co-authored-by: modmuss50 --- enigma-cli/build.gradle | 5 + .../enigma/command/CheckMappingsCommand.java | 77 +++++++++++ .../main/java/cuchaz/enigma/command/Command.java | 154 +++++++++++++++++++++ .../enigma/command/ComposeMappingsCommand.java | 42 ++++++ .../enigma/command/ConvertMappingsCommand.java | 39 ++++++ .../cuchaz/enigma/command/DecompileCommand.java | 54 ++++++++ .../cuchaz/enigma/command/DeobfuscateCommand.java | 37 +++++ .../enigma/command/InvertMappingsCommand.java | 41 ++++++ .../src/main/java/cuchaz/enigma/command/Main.java | 105 ++++++++++++++ .../command/MapSpecializedMethodsCommand.java | 71 ++++++++++ .../cuchaz/enigma/command/MappingCommandsUtil.java | 87 ++++++++++++ .../enigma/command/CheckMappingsCommandTest.java | 21 +++ .../correctMappings/base/Base.mapping | 1 + .../packageAccess/correctMappings/base/One.mapping | 1 + .../packageAccess/correctMappings/two/Two.mapping | 1 + .../packageAccess/wrongMappings/base/Base.mapping | 1 + .../packageAccess/wrongMappings/one/One.mapping | 1 + .../packageAccess/wrongMappings/two/Two.mapping | 1 + 18 files changed, 739 insertions(+) create mode 100644 enigma-cli/build.gradle create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/Command.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/Main.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java create mode 100644 enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java create mode 100644 enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java create mode 100644 enigma-cli/src/test/resources/packageAccess/correctMappings/base/Base.mapping create mode 100644 enigma-cli/src/test/resources/packageAccess/correctMappings/base/One.mapping create mode 100644 enigma-cli/src/test/resources/packageAccess/correctMappings/two/Two.mapping create mode 100644 enigma-cli/src/test/resources/packageAccess/wrongMappings/base/Base.mapping create mode 100644 enigma-cli/src/test/resources/packageAccess/wrongMappings/one/One.mapping create mode 100644 enigma-cli/src/test/resources/packageAccess/wrongMappings/two/Two.mapping (limited to 'enigma-cli') diff --git a/enigma-cli/build.gradle b/enigma-cli/build.gradle new file mode 100644 index 0000000..3ee1239 --- /dev/null +++ b/enigma-cli/build.gradle @@ -0,0 +1,5 @@ +dependencies { + implementation project(':enigma') +} + +jar.manifest.attributes 'Main-Class': 'cuchaz.enigma.command.Main' diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java new file mode 100644 index 0000000..e4deef8 --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java @@ -0,0 +1,77 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.Enigma; +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.serde.MappingFormat; +import cuchaz.enigma.translation.mapping.tree.EntryTree; +import cuchaz.enigma.translation.representation.entry.ClassEntry; + +import java.nio.file.Path; +import java.util.Set; +import java.util.stream.Collectors; + +public class CheckMappingsCommand extends Command { + + public CheckMappingsCommand() { + super("checkmappings"); + } + + @Override + public String getUsage() { + return " "; + } + + @Override + public boolean isValidArgument(int length) { + return length == 2; + } + + @Override + public void run(String... args) throws Exception { + Path fileJarIn = getReadableFile(getArg(args, 0, "in jar", true)).toPath(); + Path fileMappings = getReadablePath(getArg(args, 1, "mappings file", true)); + + Enigma enigma = Enigma.create(); + + System.out.println("Reading JAR..."); + + EnigmaProject project = enigma.openJar(fileJarIn, ProgressListener.none()); + + System.out.println("Reading mappings..."); + + MappingFormat format = chooseEnigmaFormat(fileMappings); + MappingSaveParameters saveParameters = enigma.getProfile().getMappingSaveParameters(); + + EntryTree mappings = format.read(fileMappings, ProgressListener.none(), saveParameters); + project.setMappings(mappings); + + JarIndex idx = project.getJarIndex(); + + boolean error = false; + + for (Set partition : idx.getPackageVisibilityIndex().getPartitions()) { + long packages = partition.stream() + .map(project.getMapper()::deobfuscate) + .map(ClassEntry::getPackageName) + .distinct() + .count(); + if (packages > 1) { + error = true; + System.err.println("ERROR: Must be in one package:\n" + partition.stream() + .map(project.getMapper()::deobfuscate) + .map(ClassEntry::toString) + .sorted() + .collect(Collectors.joining("\n")) + ); + } + } + + if (error) { + throw new IllegalStateException("Errors in package visibility detected, see SysErr above"); + } + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java b/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java new file mode 100644 index 0000000..0640e3e --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java @@ -0,0 +1,154 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.Enigma; +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.serde.MappingFormat; +import cuchaz.enigma.translation.mapping.tree.EntryTree; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.google.common.io.MoreFiles; + +public abstract class Command { + public final String name; + + protected Command(String name) { + this.name = name; + } + + public abstract String getUsage(); + + public abstract boolean isValidArgument(int length); + + public abstract void run(String... args) throws Exception; + + protected static EnigmaProject openProject(Path fileJarIn, Path fileMappings) throws Exception { + ProgressListener progress = new ConsoleProgressListener(); + + Enigma enigma = Enigma.create(); + + System.out.println("Reading jar..."); + EnigmaProject project = enigma.openJar(fileJarIn, progress); + + if (fileMappings != null) { + System.out.println("Reading mappings..."); + + MappingSaveParameters saveParameters = enigma.getProfile().getMappingSaveParameters(); + EntryTree mappings = chooseEnigmaFormat(fileMappings).read(fileMappings, progress, saveParameters); + + project.setMappings(mappings); + } + + return project; + } + + protected static MappingFormat chooseEnigmaFormat(Path path) { + if (Files.isDirectory(path)) { + return MappingFormat.ENIGMA_DIRECTORY; + } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(path))) { + return MappingFormat.ENIGMA_ZIP; + } else { + return MappingFormat.ENIGMA_FILE; + } + } + + protected static File getWritableFile(String path) { + if (path == null) { + return null; + } + File file = new File(path).getAbsoluteFile(); + File dir = file.getParentFile(); + if (dir == null) { + throw new IllegalArgumentException("Cannot write file: " + path); + } + // quick fix to avoid stupid stuff in Gradle code + if (!dir.isDirectory()) { + dir.mkdirs(); + } + return file; + } + + protected static File getWritableFolder(String path) { + if (path == null) { + return null; + } + File dir = new File(path).getAbsoluteFile(); + if (!dir.exists()) { + throw new IllegalArgumentException("Cannot write to folder: " + dir); + } + return dir; + } + + protected static File getReadableFile(String path) { + if (path == null) { + return null; + } + File file = new File(path).getAbsoluteFile(); + if (!file.exists()) { + throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); + } + return file; + } + + protected static Path getReadablePath(String path) { + if (path == null) { + return null; + } + Path file = Paths.get(path).toAbsolutePath(); + if (!Files.exists(file)) { + throw new IllegalArgumentException("Cannot find file: " + file.toString()); + } + return file; + } + + protected static String getArg(String[] args, int i, String name, boolean required) { + if (i >= args.length) { + if (required) { + throw new IllegalArgumentException(name + " is required"); + } else { + return null; + } + } + return args[i]; + } + + public static class ConsoleProgressListener implements ProgressListener { + + private static final int ReportTime = 5000; // 5s + + private int totalWork; + private long startTime; + private long lastReportTime; + + @Override + public void init(int totalWork, String title) { + this.totalWork = totalWork; + this.startTime = System.currentTimeMillis(); + this.lastReportTime = this.startTime; + System.out.println(title); + } + + @Override + public void step(int numDone, String message) { + long now = System.currentTimeMillis(); + boolean isLastUpdate = numDone == this.totalWork; + boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime; + + if (shouldReport) { + int percent = numDone * 100 / this.totalWork; + System.out.println(String.format("\tProgress: %3d%%", percent)); + this.lastReportTime = now; + } + if (isLastUpdate) { + double elapsedSeconds = (now - this.startTime) / 1000.0; + System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); + } + } + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java new file mode 100644 index 0000000..e10fd47 --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java @@ -0,0 +1,42 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.translation.mapping.MappingOperations; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.tree.EntryTree; +import cuchaz.enigma.utils.Utils; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class ComposeMappingsCommand extends Command { + public ComposeMappingsCommand() { + super("compose-mappings"); + } + + @Override + public String getUsage() { + return " "; + } + + @Override + public boolean isValidArgument(int length) { + return length == 7; + } + + @Override + public void run(String... args) throws IOException, MappingParseException { + MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + + EntryTree left = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); + EntryTree right = MappingCommandsUtil.read(args[2], Paths.get(args[3]), saveParameters); + EntryTree result = MappingOperations.compose(left, right, args[6].equals("left") || args[6].equals("both"), args[6].equals("right") || args[6].equals("both")); + + Path output = Paths.get(args[5]); + Utils.delete(output); + MappingCommandsUtil.write(result, args[4], output, saveParameters); + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java new file mode 100644 index 0000000..144d89c --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java @@ -0,0 +1,39 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.tree.EntryTree; +import cuchaz.enigma.utils.Utils; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class ConvertMappingsCommand extends Command { + public ConvertMappingsCommand() { + super("convert-mappings"); + } + + @Override + public String getUsage() { + return " "; + } + + @Override + public boolean isValidArgument(int length) { + return length == 4; + } + + @Override + public void run(String... args) throws IOException, MappingParseException { + MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + + EntryTree mappings = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); + + Path output = Paths.get(args[3]); + Utils.delete(output); + MappingCommandsUtil.write(mappings, args[2], output, saveParameters); + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java new file mode 100644 index 0000000..3d15dac --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java @@ -0,0 +1,54 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.source.DecompilerService; +import cuchaz.enigma.source.Decompilers; + +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.Locale; + +public class DecompileCommand extends Command { + + public DecompileCommand() { + super("decompile"); + } + + @Override + public String getUsage() { + return " []"; + } + + @Override + public boolean isValidArgument(int length) { + return length == 2 || length == 3; + } + + @Override + public void run(String... args) throws Exception { + String decompilerName = getArg(args, 1, "decompiler", true); + Path fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)).toPath(); + Path fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)).toPath(); + Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); + + DecompilerService decompilerService; + + try { + Field decompilerField = Decompilers.class.getField(decompilerName.toUpperCase(Locale.ROOT)); + decompilerService = (DecompilerService) decompilerField.get(null); + } catch (NoSuchFieldException e) { + System.err.println("Decompiler not found."); + return; + } + + EnigmaProject project = openProject(fileJarIn, fileMappings); + + ProgressListener progress = new ConsoleProgressListener(); + + EnigmaProject.JarExport jar = project.exportRemappedJar(progress); + EnigmaProject.SourceExport source = jar.decompile(progress, decompilerService); + + source.write(fileJarOut, progress); + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java new file mode 100644 index 0000000..b0d2a7d --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java @@ -0,0 +1,37 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.ProgressListener; + +import java.nio.file.Path; + +public class DeobfuscateCommand extends Command { + + public DeobfuscateCommand() { + super("deobfuscate"); + } + + @Override + public String getUsage() { + return " []"; + } + + @Override + public boolean isValidArgument(int length) { + return length == 2 || length == 3; + } + + @Override + public void run(String... args) throws Exception { + Path fileJarIn = getReadablePath(getArg(args, 0, "in jar", true)); + Path fileJarOut = getWritableFile(getArg(args, 1, "out jar", true)).toPath(); + Path fileMappings = getReadablePath(getArg(args, 2, "mappings file", false)); + + EnigmaProject project = openProject(fileJarIn, fileMappings); + + ProgressListener progress = new ConsoleProgressListener(); + + EnigmaProject.JarExport jar = project.exportRemappedJar(progress); + jar.write(fileJarOut, progress); + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java new file mode 100644 index 0000000..0780a96 --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java @@ -0,0 +1,41 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.translation.mapping.MappingOperations; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.tree.EntryTree; +import cuchaz.enigma.utils.Utils; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class InvertMappingsCommand extends Command { + public InvertMappingsCommand() { + super("invert-mappings"); + } + + @Override + public String getUsage() { + return " "; + } + + @Override + public boolean isValidArgument(int length) { + return length == 4; + } + + @Override + public void run(String... args) throws IOException, MappingParseException { + MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + + EntryTree source = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); + EntryTree result = MappingOperations.invert(source); + + Path output = Paths.get(args[3]); + Utils.delete(output); + MappingCommandsUtil.write(result, args[2], output, saveParameters); + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java b/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java new file mode 100644 index 0000000..0a4c1b9 --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.command; + +import cuchaz.enigma.Enigma; + +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; + +public class Main { + + private static final Map COMMANDS = new LinkedHashMap<>(); + + public static void main(String... args) throws Exception { + try { + // process the command + if (args.length < 1) + throw new IllegalArgumentException("Requires a command"); + String command = args[0].toLowerCase(Locale.ROOT); + + Command cmd = COMMANDS.get(command); + if (cmd == null) + throw new IllegalArgumentException("Command not recognized: " + command); + + if (!cmd.isValidArgument(args.length - 1)) { + throw new CommandHelpException(cmd); + } + + String[] cmdArgs = new String[args.length - 1]; + System.arraycopy(args, 1, cmdArgs, 0, args.length - 1); + + try { + cmd.run(cmdArgs); + } catch (Exception ex) { + throw new CommandHelpException(cmd, ex); + } + } catch (CommandHelpException ex) { + System.err.println(ex.getMessage()); + System.out.println(String.format("%s - %s", Enigma.NAME, Enigma.VERSION)); + System.out.println("Command " + ex.command.name + " has encountered an error! Usage:"); + printHelp(ex.command); + System.exit(1); + } catch (IllegalArgumentException ex) { + System.err.println(ex.getMessage()); + printHelp(); + System.exit(1); + } + } + + private static void printHelp() { + System.out.println(String.format("%s - %s", Enigma.NAME, Enigma.VERSION)); + System.out.println("Usage:"); + System.out.println("\tjava -cp enigma.jar cuchaz.enigma.command.CommandMain "); + System.out.println("\twhere is one of:"); + + for (Command command : COMMANDS.values()) { + printHelp(command); + } + } + + private static void printHelp(Command command) { + System.out.println("\t\t" + command.name + " " + command.getUsage()); + } + + private static void register(Command command) { + Command old = COMMANDS.put(command.name, command); + if (old != null) { + System.err.println("Command " + old + " with name " + command.name + " has been substituted by " + command); + } + } + + static { + register(new DeobfuscateCommand()); + register(new DecompileCommand()); + register(new ConvertMappingsCommand()); + register(new ComposeMappingsCommand()); + register(new InvertMappingsCommand()); + register(new CheckMappingsCommand()); + register(new MapSpecializedMethodsCommand()); + } + + private static final class CommandHelpException extends IllegalArgumentException { + + final Command command; + + CommandHelpException(Command command) { + this.command = command; + } + + CommandHelpException(Command command, Throwable cause) { + super(cause); + this.command = command; + } + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java new file mode 100644 index 0000000..292de19 --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java @@ -0,0 +1,71 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.analysis.ClassCache; +import cuchaz.enigma.analysis.index.BridgeMethodIndex; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import cuchaz.enigma.translation.MappingTranslator; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.*; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.tree.EntryTree; +import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; +import cuchaz.enigma.translation.mapping.tree.HashEntryTree; +import cuchaz.enigma.translation.representation.entry.MethodEntry; +import cuchaz.enigma.utils.Utils; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +public class MapSpecializedMethodsCommand extends Command { + public MapSpecializedMethodsCommand() { + super("map-specialized-methods"); + } + + @Override + public String getUsage() { + return " "; + } + + @Override + public boolean isValidArgument(int length) { + return length == 5; + } + + @Override + public void run(String... args) throws IOException, MappingParseException { + run(Paths.get(args[0]), args[1], Paths.get(args[2]), args[3], Paths.get(args[4])); + } + + public static void run(Path jar, String sourceFormat, Path sourcePath, String resultFormat, Path output) throws IOException, MappingParseException { + MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + EntryTree source = MappingCommandsUtil.read(sourceFormat, sourcePath, saveParameters); + EntryTree result = new HashEntryTree<>(); + ClassCache classCache = ClassCache.of(jar); + JarIndex jarIndex = classCache.index(ProgressListener.none()); + BridgeMethodIndex bridgeMethodIndex = jarIndex.getBridgeMethodIndex(); + Translator translator = new MappingTranslator(source, jarIndex.getEntryResolver()); + + // Copy all non-specialized methods + for (EntryTreeNode node : source) { + if (!(node.getEntry() instanceof MethodEntry) || !bridgeMethodIndex.isSpecializedMethod((MethodEntry) node.getEntry())) { + result.insert(node.getEntry(), node.getValue()); + } + } + + // Add correct mappings for specialized methods + for (Map.Entry entry : bridgeMethodIndex.getBridgeToSpecialized().entrySet()) { + MethodEntry bridge = entry.getKey(); + MethodEntry specialized = entry.getValue(); + String name = translator.translate(bridge).getName(); + result.insert(specialized, new EntryMapping(name)); + } + + Utils.delete(output); + MappingCommandsUtil.write(result, resultFormat, output, saveParameters); + } +} diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java b/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java new file mode 100644 index 0000000..d365129 --- /dev/null +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java @@ -0,0 +1,87 @@ +package cuchaz.enigma.command; + +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.serde.*; +import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; +import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsWriter; +import cuchaz.enigma.translation.mapping.serde.tiny.TinyMappingsReader; +import cuchaz.enigma.translation.mapping.serde.tiny.TinyMappingsWriter; +import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; +import cuchaz.enigma.translation.mapping.tree.EntryTree; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public final class MappingCommandsUtil { + private MappingCommandsUtil() {} + + public static EntryTree read(String type, Path path, MappingSaveParameters saveParameters) throws MappingParseException, IOException { + if (type.equals("enigma")) { + return (Files.isDirectory(path) ? EnigmaMappingsReader.DIRECTORY : EnigmaMappingsReader.ZIP).read(path, ProgressListener.none(), saveParameters); + } + + if (type.equals("tiny")) { + return TinyMappingsReader.INSTANCE.read(path, ProgressListener.none(), saveParameters); + } + + MappingFormat format = null; + try { + format = MappingFormat.valueOf(type.toUpperCase()); + } catch (IllegalArgumentException ignored) { + if (type.equals("tinyv2")) { + format = MappingFormat.TINY_V2; + } + } + + if (format != null) { + return format.getReader().read(path, ProgressListener.none(), saveParameters); + } + + throw new IllegalArgumentException("no reader for " + type); + } + + public static void write(EntryTree mappings, String type, Path path, MappingSaveParameters saveParameters) { + if (type.equals("enigma")) { + EnigmaMappingsWriter.DIRECTORY.write(mappings, path, ProgressListener.none(), saveParameters); + return; + } + + if (type.startsWith("tinyv2:") || type.startsWith("tiny_v2:")) { + String[] split = type.split(":"); + + if (split.length != 3) { + throw new IllegalArgumentException("specify column names as 'tinyv2:from_namespace:to_namespace'"); + } + + new TinyV2Writer(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters); + return; + } + + if (type.startsWith("tiny:")) { + String[] split = type.split(":"); + + if (split.length != 3) { + throw new IllegalArgumentException("specify column names as 'tiny:from_column:to_column'"); + } + + new TinyMappingsWriter(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters); + return; + } + + MappingFormat format = null; + try { + format = MappingFormat.valueOf(type.toUpperCase()); + } catch (IllegalArgumentException ignored) {} + + if (format != null) { + format.getWriter().write(mappings, path, ProgressListener.none(), saveParameters); + return; + } + + throw new IllegalArgumentException("no writer for " + type); + } +} diff --git a/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java b/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java new file mode 100644 index 0000000..a29bba4 --- /dev/null +++ b/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java @@ -0,0 +1,21 @@ +package cuchaz.enigma.command; + +import org.junit.Test; + +import java.io.File; + +public class CheckMappingsCommandTest { + private static final String PACKAGE_ACCESS = "../enigma/build/test-obf/packageAccess.jar"; + + @Test(expected = IllegalStateException.class) + public void testWrong() throws Exception { + new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + + "/packageAccess/wrongMappings").getAbsolutePath()); + } + + @Test + public void testRight() throws Exception { + new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + + "/packageAccess/correctMappings").getAbsolutePath()); + } +} diff --git a/enigma-cli/src/test/resources/packageAccess/correctMappings/base/Base.mapping b/enigma-cli/src/test/resources/packageAccess/correctMappings/base/Base.mapping new file mode 100644 index 0000000..0a86def --- /dev/null +++ b/enigma-cli/src/test/resources/packageAccess/correctMappings/base/Base.mapping @@ -0,0 +1 @@ +CLASS a base/Base diff --git a/enigma-cli/src/test/resources/packageAccess/correctMappings/base/One.mapping b/enigma-cli/src/test/resources/packageAccess/correctMappings/base/One.mapping new file mode 100644 index 0000000..dd4c208 --- /dev/null +++ b/enigma-cli/src/test/resources/packageAccess/correctMappings/base/One.mapping @@ -0,0 +1 @@ +CLASS b base/One diff --git a/enigma-cli/src/test/resources/packageAccess/correctMappings/two/Two.mapping b/enigma-cli/src/test/resources/packageAccess/correctMappings/two/Two.mapping new file mode 100644 index 0000000..a179349 --- /dev/null +++ b/enigma-cli/src/test/resources/packageAccess/correctMappings/two/Two.mapping @@ -0,0 +1 @@ +CLASS c two/Two diff --git a/enigma-cli/src/test/resources/packageAccess/wrongMappings/base/Base.mapping b/enigma-cli/src/test/resources/packageAccess/wrongMappings/base/Base.mapping new file mode 100644 index 0000000..0a86def --- /dev/null +++ b/enigma-cli/src/test/resources/packageAccess/wrongMappings/base/Base.mapping @@ -0,0 +1 @@ +CLASS a base/Base diff --git a/enigma-cli/src/test/resources/packageAccess/wrongMappings/one/One.mapping b/enigma-cli/src/test/resources/packageAccess/wrongMappings/one/One.mapping new file mode 100644 index 0000000..15b42cf --- /dev/null +++ b/enigma-cli/src/test/resources/packageAccess/wrongMappings/one/One.mapping @@ -0,0 +1 @@ +CLASS b one/One diff --git a/enigma-cli/src/test/resources/packageAccess/wrongMappings/two/Two.mapping b/enigma-cli/src/test/resources/packageAccess/wrongMappings/two/Two.mapping new file mode 100644 index 0000000..a179349 --- /dev/null +++ b/enigma-cli/src/test/resources/packageAccess/wrongMappings/two/Two.mapping @@ -0,0 +1 @@ +CLASS c two/Two -- cgit v1.2.3