summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/cuchaz/enigma/CommandMain.java2
-rw-r--r--src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java37
-rw-r--r--src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java64
-rw-r--r--src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java36
-rw-r--r--src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java152
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsWriter.java144
-rw-r--r--src/main/java/cuchaz/enigma/utils/Utils.java13
7 files changed, 409 insertions, 39 deletions
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java
index 5b25087..5e7a1af 100644
--- a/src/main/java/cuchaz/enigma/CommandMain.java
+++ b/src/main/java/cuchaz/enigma/CommandMain.java
@@ -83,6 +83,8 @@ public class CommandMain {
83 register(new DeobfuscateCommand()); 83 register(new DeobfuscateCommand());
84 register(new DecompileCommand()); 84 register(new DecompileCommand());
85 register(new ConvertMappingsCommand()); 85 register(new ConvertMappingsCommand());
86 register(new ComposeMappingsCommand());
87 register(new InvertMappingsCommand());
86 register(new CheckMappingsCommand()); 88 register(new CheckMappingsCommand());
87 } 89 }
88 90
diff --git a/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java b/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java
new file mode 100644
index 0000000..1f6a069
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java
@@ -0,0 +1,37 @@
1package cuchaz.enigma.command;
2
3import cuchaz.enigma.throwables.MappingParseException;
4import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.tree.EntryTree;
6import cuchaz.enigma.utils.Utils;
7
8import java.io.IOException;
9import java.nio.file.Path;
10import java.nio.file.Paths;
11
12public class ComposeMappingsCommand extends Command {
13 public ComposeMappingsCommand() {
14 super("compose-mappings");
15 }
16
17 @Override
18 public String getUsage() {
19 return "<left-format> <left> <right-format> <right> <result-format> <result> <keep-mode>";
20 }
21
22 @Override
23 public boolean isValidArgument(int length) {
24 return length == 7;
25 }
26
27 @Override
28 public void run(String... args) throws IOException, MappingParseException {
29 EntryTree<EntryMapping> left = MappingCommandsUtil.read(args[0], Paths.get(args[1]));
30 EntryTree<EntryMapping> right = MappingCommandsUtil.read(args[2], Paths.get(args[3]));
31 EntryTree<EntryMapping> result = MappingCommandsUtil.compose(left, right, args[6].equals("left") || args[6].equals("both"), args[6].equals("right") || args[6].equals("both"));
32
33 Path output = Paths.get(args[5]);
34 Utils.delete(output);
35 MappingCommandsUtil.write(result, args[4], output);
36 }
37}
diff --git a/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java b/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java
index 75d3791..775bd3e 100644
--- a/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java
+++ b/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java
@@ -1,47 +1,35 @@
1package cuchaz.enigma.command; 1package cuchaz.enigma.command;
2 2
3import cuchaz.enigma.throwables.MappingParseException;
3import cuchaz.enigma.translation.mapping.EntryMapping; 4import cuchaz.enigma.translation.mapping.EntryMapping;
4import cuchaz.enigma.translation.mapping.serde.MappingFormat;
5import cuchaz.enigma.translation.mapping.tree.EntryTree; 5import cuchaz.enigma.translation.mapping.tree.EntryTree;
6import cuchaz.enigma.utils.Utils;
6 7
7import java.io.File; 8import java.io.IOException;
8import java.nio.file.Path; 9import java.nio.file.Path;
9import java.util.Locale; 10import java.nio.file.Paths;
10 11
11public class ConvertMappingsCommand extends Command { 12public class ConvertMappingsCommand extends Command {
12 13 public ConvertMappingsCommand() {
13 public ConvertMappingsCommand() { 14 super("convert-mappings");
14 super("convertmappings"); 15 }
15 } 16
16 17 @Override
17 @Override 18 public String getUsage() {
18 public String getUsage() { 19 return "<source-format> <source> <result-format> <result>";
19 return "<enigma mappings> <converted mappings> <ENIGMA_FILE|ENIGMA_DIRECTORY|SRG_FILE>"; 20 }
20 } 21
21 22 @Override
22 @Override 23 public boolean isValidArgument(int length) {
23 public boolean isValidArgument(int length) { 24 return length == 4;
24 return length == 3; 25 }
25 } 26
26 27 @Override
27 @Override 28 public void run(String... args) throws IOException, MappingParseException {
28 public void run(String... args) throws Exception { 29 EntryTree<EntryMapping> mappings = MappingCommandsUtil.read(args[0], Paths.get(args[1]));
29 Path fileMappings = getReadablePath(getArg(args, 0, "enigma mappings", true)); 30
30 File result = getWritableFile(getArg(args, 1, "converted mappings", true)); 31 Path output = Paths.get(args[3]);
31 String name = getArg(args, 2, "format desc", true); 32 Utils.delete(output);
32 MappingFormat saveFormat; 33 MappingCommandsUtil.write(mappings, args[2], output);
33 try { 34 }
34 saveFormat = MappingFormat.valueOf(name.toUpperCase(Locale.ROOT));
35 } catch (IllegalArgumentException e) {
36 throw new IllegalArgumentException(name + "is not a valid mapping format!");
37 }
38
39 System.out.println("Reading mappings...");
40
41 MappingFormat readFormat = chooseEnigmaFormat(fileMappings);
42 EntryTree<EntryMapping> mappings = readFormat.read(fileMappings, new ConsoleProgressListener());
43 System.out.println("Saving new mappings...");
44
45 saveFormat.write(mappings, result.toPath(), new ConsoleProgressListener());
46 }
47} 35}
diff --git a/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java b/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java
new file mode 100644
index 0000000..bfe8308
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java
@@ -0,0 +1,36 @@
1package cuchaz.enigma.command;
2
3import cuchaz.enigma.throwables.MappingParseException;
4import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.tree.EntryTree;
6import cuchaz.enigma.utils.Utils;
7
8import java.io.IOException;
9import java.nio.file.Path;
10import java.nio.file.Paths;
11
12public class InvertMappingsCommand extends Command {
13 public InvertMappingsCommand() {
14 super("invert-mappings");
15 }
16
17 @Override
18 public String getUsage() {
19 return "<source-format> <source> <result-format> <result>";
20 }
21
22 @Override
23 public boolean isValidArgument(int length) {
24 return length == 4;
25 }
26
27 @Override
28 public void run(String... args) throws IOException, MappingParseException {
29 EntryTree<EntryMapping> source = MappingCommandsUtil.read(args[0], Paths.get(args[1]));
30 EntryTree<EntryMapping> result = MappingCommandsUtil.invert(source);
31
32 Path output = Paths.get(args[3]);
33 Utils.delete(output);
34 MappingCommandsUtil.write(result, args[2], output);
35 }
36}
diff --git a/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java b/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java
new file mode 100644
index 0000000..4679575
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java
@@ -0,0 +1,152 @@
1package cuchaz.enigma.command;
2
3import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.throwables.MappingParseException;
5import cuchaz.enigma.translation.MappingTranslator;
6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.VoidEntryResolver;
9import cuchaz.enigma.translation.mapping.serde.*;
10import cuchaz.enigma.translation.mapping.tree.EntryTree;
11import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
12import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
13import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.Entry;
15import cuchaz.enigma.translation.representation.entry.FieldEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
17
18import java.io.IOException;
19import java.nio.file.Path;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Map;
23import java.util.Set;
24
25public final class MappingCommandsUtil {
26 public static void main(String[] args) throws Exception {
27 new InvertMappingsCommand().run(
28 "enigma",
29 "D:\\IdeaProjects\\yarn\\mappings",
30 "enigma",
31 "D:\\IdeaProjects\\Enigma\\converted");
32 }
33
34 private MappingCommandsUtil() {}
35
36 public static EntryTree<EntryMapping> invert(EntryTree<EntryMapping> mappings) {
37 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
38 EntryTree<EntryMapping> result = new HashEntryTree<>();
39
40 for (EntryTreeNode<EntryMapping> node : mappings) {
41 Entry<?> leftEntry = node.getEntry();
42 EntryMapping leftMapping = node.getValue();
43
44 if (!(leftEntry instanceof ClassEntry || leftEntry instanceof MethodEntry || leftEntry instanceof FieldEntry)) {
45 result.insert(translator.translate(leftEntry), leftMapping);
46 continue;
47 }
48
49 Entry<?> rightEntry = translator.translate(leftEntry);
50
51 result.insert(rightEntry, leftMapping == null ? null : new EntryMapping(leftEntry.getName())); // TODO: leftMapping.withName once javadoc PR is merged
52 }
53
54 return result;
55 }
56
57 @SuppressWarnings("unchecked")
58 public static EntryTree<EntryMapping> compose(EntryTree<EntryMapping> left, EntryTree<EntryMapping> right, boolean keepLeftOnly, boolean keepRightOnly) {
59 Translator leftTranslator = new MappingTranslator(left, VoidEntryResolver.INSTANCE);
60 EntryTree<EntryMapping> result = new HashEntryTree<>();
61 Map<Entry<?>, Entry<?>> rightToLeft = new HashMap<>();
62 Set<Entry<?>> addedMappings = new HashSet<>();
63
64 for (EntryTreeNode<EntryMapping> node : left) {
65 Entry<?> leftEntry = node.getEntry();
66 EntryMapping leftMapping = node.getValue();
67
68 Entry<?> rightEntry = leftTranslator.translate(leftEntry);
69 rightToLeft.put(rightEntry, leftEntry);
70
71 EntryMapping rightMapping = right.get(rightEntry);
72 if (rightMapping != null) {
73 result.insert(leftEntry, rightMapping);
74 addedMappings.add(rightEntry);
75 } else if (keepLeftOnly) {
76 result.insert(leftEntry, leftMapping);
77 }
78 }
79
80 if (keepRightOnly) {
81 for (EntryTreeNode<EntryMapping> node : right) {
82 Entry<?> rightEntry = node.getEntry();
83 EntryMapping rightMapping = node.getValue();
84
85 if (addedMappings.contains(rightEntry)) {
86 continue;
87 }
88
89 Entry<?> parent = rightEntry.getParent();
90 Entry<?> correctEntry = rightEntry;
91 if (rightToLeft.containsKey(parent)) {
92 correctEntry = ((Entry<Entry<?>>) rightEntry).withParent(rightToLeft.get(parent));
93 }
94
95 result.insert(correctEntry, rightMapping);
96 rightToLeft.put(rightEntry, correctEntry);
97 }
98 }
99 return result;
100 }
101
102 public static EntryTree<EntryMapping> read(String type, Path path) throws MappingParseException, IOException {
103 if (type.equals("enigma")) {
104 return EnigmaMappingsReader.DIRECTORY.read(path, ProgressListener.none());
105 }
106
107 if (type.equals("tiny")) {
108 return TinyMappingsReader.INSTANCE.read(path, ProgressListener.none());
109 }
110
111 MappingFormat format = null;
112 try {
113 format = MappingFormat.valueOf(type.toUpperCase());
114 } catch (IllegalArgumentException ignored) {}
115
116 if (format != null) {
117 return format.getReader().read(path, ProgressListener.none());
118 }
119
120 throw new IllegalArgumentException("no reader for " + type);
121 }
122
123 public static void write(EntryTree<EntryMapping> mappings, String type, Path path) {
124 if (type.equals("enigma")) {
125 EnigmaMappingsWriter.DIRECTORY.write(mappings, path, ProgressListener.none());
126 return;
127 }
128
129 if (type.startsWith("tiny")) {
130 String[] split = type.split(":");
131
132 if (split.length != 3) {
133 throw new IllegalArgumentException("specify column names as 'tiny:from_column:to_column'");
134 }
135
136 new TinyMappingsWriter(split[1], split[2]).write(mappings, path, ProgressListener.none());
137 return;
138 }
139
140 MappingFormat format = null;
141 try {
142 format = MappingFormat.valueOf(type.toUpperCase());
143 } catch (IllegalArgumentException ignored) {}
144
145 if (format != null) {
146 format.getWriter().write(mappings, path, ProgressListener.none());
147 return;
148 }
149
150 throw new IllegalArgumentException("no writer for " + type);
151 }
152}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsWriter.java
new file mode 100644
index 0000000..0a52dad
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsWriter.java
@@ -0,0 +1,144 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.base.Joiner;
4import com.google.common.collect.Lists;
5import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.translation.MappingTranslator;
7import cuchaz.enigma.translation.Translator;
8import cuchaz.enigma.translation.mapping.EntryMapping;
9import cuchaz.enigma.translation.mapping.MappingDelta;
10import cuchaz.enigma.translation.mapping.VoidEntryResolver;
11import cuchaz.enigma.translation.mapping.tree.EntryTree;
12import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
13import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.Entry;
15import cuchaz.enigma.translation.representation.entry.FieldEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
17
18import java.io.BufferedWriter;
19import java.io.IOException;
20import java.io.Writer;
21import java.nio.charset.StandardCharsets;
22import java.nio.file.Files;
23import java.nio.file.Path;
24import java.util.Comparator;
25import java.util.HashSet;
26import java.util.Set;
27
28public class TinyMappingsWriter implements MappingsWriter {
29 private static final String VERSION_CONSTANT = "v1";
30 private static final Joiner TAB_JOINER = Joiner.on('\t');
31
32 // HACK: as of enigma 0.13.1, some fields seem to appear duplicated?
33 private final Set<String> writtenLines = new HashSet<>();
34 private final String nameObf;
35 private final String nameDeobf;
36
37 public TinyMappingsWriter(String nameObf, String nameDeobf) {
38 this.nameObf = nameObf;
39 this.nameDeobf = nameDeobf;
40 }
41
42 @Override
43 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress) {
44 try {
45 Files.deleteIfExists(path);
46 Files.createFile(path);
47 } catch (IOException e) {
48 e.printStackTrace();
49 }
50
51 try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
52 writeLine(writer, new String[]{VERSION_CONSTANT, nameObf, nameDeobf});
53
54 Lists.newArrayList(mappings).stream()
55 .map(EntryTreeNode::getEntry).sorted(Comparator.comparing(Object::toString))
56 .forEach(entry -> writeEntry(writer, mappings, entry));
57 } catch (IOException e) {
58 e.printStackTrace();
59 }
60 }
61
62 private void writeEntry(Writer writer, EntryTree<EntryMapping> mappings, Entry<?> entry) {
63 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
64 if (node == null) {
65 return;
66 }
67
68 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
69
70 EntryMapping mapping = mappings.get(entry);
71 if (mapping != null && !entry.getName().equals(mapping.getTargetName())) {
72 if (entry instanceof ClassEntry) {
73 writeClass(writer, (ClassEntry) entry, translator);
74 } else if (entry instanceof FieldEntry) {
75 writeLine(writer, serializeEntry(entry, mapping.getTargetName()));
76 } else if (entry instanceof MethodEntry) {
77 writeLine(writer, serializeEntry(entry, mapping.getTargetName()));
78 }
79 }
80
81 writeChildren(writer, mappings, node);
82 }
83
84 private void writeChildren(Writer writer, EntryTree<EntryMapping> mappings, EntryTreeNode<EntryMapping> node) {
85 node.getChildren().stream()
86 .filter(e -> e instanceof FieldEntry).sorted()
87 .forEach(child -> writeEntry(writer, mappings, child));
88
89 node.getChildren().stream()
90 .filter(e -> e instanceof MethodEntry).sorted()
91 .forEach(child -> writeEntry(writer, mappings, child));
92
93 node.getChildren().stream()
94 .filter(e -> e instanceof ClassEntry).sorted()
95 .forEach(child -> writeEntry(writer, mappings, child));
96 }
97
98 private void writeClass(Writer writer, ClassEntry entry, Translator translator) {
99 ClassEntry translatedEntry = translator.translate(entry);
100
101 String obfClassName = entry.getFullName();
102 String deobfClassName = translatedEntry.getFullName();
103 writeLine(writer, new String[]{"CLASS", obfClassName, deobfClassName});
104 }
105
106 private void writeLine(Writer writer, String[] data) {
107 try {
108 String line = TAB_JOINER.join(data) + "\n";
109 if (writtenLines.add(line)) {
110 writer.write(line);
111 }
112 } catch (IOException e) {
113 throw new RuntimeException(e);
114 }
115 }
116
117 private String[] serializeEntry(Entry<?> entry, String... extraFields) {
118 String[] data = null;
119
120 if (entry instanceof FieldEntry) {
121 data = new String[4 + extraFields.length];
122 data[0] = "FIELD";
123 data[1] = entry.getContainingClass().getFullName();
124 data[2] = ((FieldEntry) entry).getDesc().toString();
125 data[3] = entry.getName();
126 } else if (entry instanceof MethodEntry) {
127 data = new String[4 + extraFields.length];
128 data[0] = "METHOD";
129 data[1] = entry.getContainingClass().getFullName();
130 data[2] = ((MethodEntry) entry).getDesc().toString();
131 data[3] = entry.getName();
132 } else if (entry instanceof ClassEntry) {
133 data = new String[2 + extraFields.length];
134 data[0] = "CLASS";
135 data[1] = ((ClassEntry) entry).getFullName();
136 }
137
138 if (data != null) {
139 System.arraycopy(extraFields, 0, data, data.length - extraFields.length, extraFields.length);
140 }
141
142 return data;
143 }
144}
diff --git a/src/main/java/cuchaz/enigma/utils/Utils.java b/src/main/java/cuchaz/enigma/utils/Utils.java
index bd09c64..6788042 100644
--- a/src/main/java/cuchaz/enigma/utils/Utils.java
+++ b/src/main/java/cuchaz/enigma/utils/Utils.java
@@ -21,8 +21,11 @@ import java.io.InputStream;
21import java.io.InputStreamReader; 21import java.io.InputStreamReader;
22import java.net.URI; 22import java.net.URI;
23import java.net.URISyntaxException; 23import java.net.URISyntaxException;
24import java.util.Arrays; 24import java.nio.file.Files;
25import java.nio.file.Path;
26import java.util.Comparator;
25import java.util.List; 27import java.util.List;
28import java.util.stream.Collectors;
26 29
27public class Utils { 30public class Utils {
28 31
@@ -94,4 +97,12 @@ public class Utils {
94 String value = System.getProperty(property); 97 String value = System.getProperty(property);
95 return value == null ? defValue : Boolean.parseBoolean(value); 98 return value == null ? defValue : Boolean.parseBoolean(value);
96 } 99 }
100
101 public static void delete(Path path) throws IOException {
102 if (path.toFile().exists()) {
103 for (Path p : Files.walk(path).sorted(Comparator.reverseOrder()).collect(Collectors.toList())) {
104 Files.delete(p);
105 }
106 }
107 }
97} 108}