summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java3
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/ProguardMappingsReader.java135
2 files changed, 137 insertions, 1 deletions
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
index 4799589..c770ddc 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
@@ -15,7 +15,8 @@ public enum MappingFormat {
15 ENIGMA_FILE(EnigmaMappingsWriter.FILE, EnigmaMappingsReader.FILE), 15 ENIGMA_FILE(EnigmaMappingsWriter.FILE, EnigmaMappingsReader.FILE),
16 ENIGMA_DIRECTORY(EnigmaMappingsWriter.DIRECTORY, EnigmaMappingsReader.DIRECTORY), 16 ENIGMA_DIRECTORY(EnigmaMappingsWriter.DIRECTORY, EnigmaMappingsReader.DIRECTORY),
17 TINY_FILE(null, TinyMappingsReader.INSTANCE), 17 TINY_FILE(null, TinyMappingsReader.INSTANCE),
18 SRG_FILE(SrgMappingsWriter.INSTANCE, null); 18 SRG_FILE(SrgMappingsWriter.INSTANCE, null),
19 PROGUARD(null, ProguardMappingsReader.INSTANCE);
19 20
20 private final MappingsWriter writer; 21 private final MappingsWriter writer;
21 private final MappingsReader reader; 22 private final MappingsReader reader;
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/ProguardMappingsReader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/ProguardMappingsReader.java
new file mode 100644
index 0000000..76f0923
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/ProguardMappingsReader.java
@@ -0,0 +1,135 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.base.Charsets;
4import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.command.MappingCommandsUtil;
6import cuchaz.enigma.throwables.MappingParseException;
7import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.MappingSaveParameters;
9import cuchaz.enigma.translation.mapping.tree.EntryTree;
10import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
11import cuchaz.enigma.translation.representation.MethodDescriptor;
12import cuchaz.enigma.translation.representation.TypeDescriptor;
13import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.FieldEntry;
15import cuchaz.enigma.translation.representation.entry.MethodEntry;
16
17import java.io.IOException;
18import java.nio.file.Files;
19import java.nio.file.Path;
20import java.util.regex.Matcher;
21import java.util.regex.Pattern;
22
23public class ProguardMappingsReader implements MappingsReader {
24 public static final ProguardMappingsReader INSTANCE = new ProguardMappingsReader();
25 private static final String NAME = "[a-zA-Z0-9_\\-.$<>]+";
26 private static final String TYPE = NAME + "(?:\\[])*";
27 private static final String TYPE_LIST = "|(?:(?:" + TYPE + ",)*" + TYPE + ")";
28 private static final Pattern CLASS = Pattern.compile("(" + NAME + ") -> (" + NAME + "):");
29 private static final Pattern FIELD = Pattern.compile(" {4}(" + TYPE + ") (" + NAME + ") -> (" + NAME + ")");
30 private static final Pattern METHOD = Pattern.compile(" {4}(?:[0-9]+:[0-9]+:)?(" + TYPE + ") (" + NAME + ")\\((" + TYPE_LIST + ")\\) -> (" + NAME + ")");
31
32 public ProguardMappingsReader() {}
33
34
35 @Override
36 public EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException {
37 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
38
39 int lineNumber = 0;
40 ClassEntry currentClass = null;
41 for (String line : Files.readAllLines(path, Charsets.UTF_8)) {
42 lineNumber++;
43
44 if (line.startsWith("#") || line.isEmpty()) {
45 continue;
46 }
47
48 Matcher classMatcher = CLASS.matcher(line);
49 Matcher fieldMatcher = FIELD.matcher(line);
50 Matcher methodMatcher = METHOD.matcher(line);
51
52 if (classMatcher.matches()) {
53 String name = classMatcher.group(1);
54 String targetName = classMatcher.group(2);
55
56 mappings.insert(currentClass = new ClassEntry(name.replace('.', '/')), new EntryMapping(ClassEntry.getInnerName(targetName.replace('.', '/'))));
57 } else if (fieldMatcher.matches()) {
58 String type = fieldMatcher.group(1);
59 String name = fieldMatcher.group(2);
60 String targetName = fieldMatcher.group(3);
61
62 if (currentClass == null) {
63 throw new MappingParseException(path::toString, lineNumber, "field mapping not inside class: " + line);
64 }
65
66 mappings.insert(new FieldEntry(currentClass, name, new TypeDescriptor(getDescriptor(type))), new EntryMapping(targetName));
67 } else if (methodMatcher.matches()) {
68 String returnType = methodMatcher.group(1);
69 String name = methodMatcher.group(2);
70 String[] parameterTypes = methodMatcher.group(3).isEmpty() ? new String[0] : methodMatcher.group(3).split(",");
71 String targetName = methodMatcher.group(4);
72
73 if (currentClass == null) {
74 throw new MappingParseException(path::toString, lineNumber, "method mapping not inside class: " + line);
75 }
76
77 mappings.insert(new MethodEntry(currentClass, name, new MethodDescriptor(getDescriptor(returnType, parameterTypes))), new EntryMapping(targetName));
78 } else {
79 throw new MappingParseException(path::toString, lineNumber, "invalid mapping line: " + line);
80 }
81 }
82
83 return MappingCommandsUtil.invert(mappings);
84 }
85
86 private String getDescriptor(String type) {
87 StringBuilder descriptor = new StringBuilder();
88
89 while (type.endsWith("[]")) {
90 descriptor.append("[");
91 type = type.substring(0, type.length() - 2);
92 }
93
94 switch (type) {
95 case "byte":
96 return descriptor + "B";
97 case "char":
98 return descriptor + "C";
99 case "short":
100 return descriptor + "S";
101 case "int":
102 return descriptor + "I";
103 case "long":
104 return descriptor + "J";
105 case "float":
106 return descriptor + "F";
107 case "double":
108 return descriptor + "D";
109 case "boolean":
110 return descriptor + "Z";
111 case "void":
112 return descriptor + "V";
113 }
114
115 descriptor.append("L");
116 descriptor.append(type.replace('.', '/'));
117 descriptor.append(";");
118
119 return descriptor.toString();
120 }
121
122 private String getDescriptor(String returnType, String[] parameterTypes) {
123 StringBuilder descriptor = new StringBuilder();
124 descriptor.append('(');
125
126 for (String parameterType : parameterTypes) {
127 descriptor.append(getDescriptor(parameterType));
128 }
129
130 descriptor.append(')');
131 descriptor.append(getDescriptor(returnType));
132
133 return descriptor.toString();
134 }
135}