1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
package cuchaz.enigma.mapping;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import cuchaz.enigma.throwables.MappingConflict;
import cuchaz.enigma.throwables.MappingParseException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class MappingsTinyReader {
public ClassMapping readClass(String[] parts) {
// Extract the inner naming of the deob form if it have one
String deobName = parts[2].contains("$") ? parts[2].substring(parts[2].lastIndexOf('$') + 1) : parts[2];
return new ClassMapping(parts[1], deobName).setDeobInner(parts[2]);
}
public FieldMapping readField(String[] parts) {
return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED);
}
public MethodMapping readMethod(String[] parts) {
return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]);
}
public Mappings read(File file) throws IOException, MappingParseException {
Mappings mappings = new Mappings(Mappings.FormatType.TINY_FILE);
List<String> lines = Files.readAllLines(file.toPath(), Charsets.UTF_8);
Map<String, ClassMapping> classMappingMap = Maps.newHashMap();
lines.remove(0); // TODO: use the header
for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
String line = lines.get(lineNumber);
String[] parts = line.split("\t");
try {
String token = parts[0];
ClassMapping classMapping;
switch (token) {
case "CLASS":
// Check for orphan created by field or method entries. It shouldn't be possible but I prefer to handle this case
if (classMappingMap.containsKey(parts[1])) {
classMapping = classMappingMap.get(parts[1]);
// We have the full deob name, Enigma only support simple class name so we extract it.
String deobName = parts[2].contains("$") ?
parts[2].substring(parts[2].lastIndexOf('$') + 1) :
parts[2];
// Add full deob name to the class mapping to handle inner class after this loop
classMappingMap.put(parts[2], classMapping.setDeobInner(parts[2]));
classMapping.setDeobfName(deobName);
// Avoid to make the mapping dirty directly at the startup
classMapping.resetDirty();
} else
classMapping = readClass(parts);
classMappingMap.put(parts[1], classMapping);
break;
case "FIELD":
// We can have missing classes mappings because they don't have a ob name, so we create it and use it
classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
classMapping.addFieldMapping(readField(parts));
break;
case "METHOD":
// We can have missing classes mappings because they don't have a ob name, so we create it and use it
classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
classMapping.addMethodMapping(readMethod(parts));
break;
default:
throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !");
}
} catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
ex.printStackTrace();
throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line);
}
}
List<ClassMapping> toRegister = new ArrayList<>(classMappingMap.values());
// After having completely parsed the file, we need to register it to the real mapping
for (ClassMapping classMapping : toRegister) {
ClassEntry obEntry = classMapping.getObfEntry();
ClassEntry deobEntry = classMapping.getDeObfEntry();
try {
if (obEntry.isInnerClass()) {
ClassMapping parent = classMappingMap.get(obEntry.getOuterClassName());
// Inner class can miss their parent... So we create it and add it to the mappings
if (parent == null) {
parent = new ClassMapping(obEntry.getOuterClassName()); // FIXME: WE ACTUALLY DON'T MANAGE INNER CLASS OF INNER CLASS
classMappingMap.put(obEntry.getOuterClassName(), parent);
mappings.addClassMapping(parent);
}
// Add the inner class to the parent
parent.addInnerClassMapping(classMapping);
}
// obf class can become deobf inner classs, manage this case.
else if (deobEntry != null && deobEntry.isInnerClass()) {
String outerClassName = deobEntry.getOuterClassName();
ClassMapping parent = classMappingMap.get(outerClassName);
// Only the inner is deob??? Okay
if (parent == null) {
parent = classMappingMap.get(outerClassName);
if (parent == null) {
parent = new ClassMapping(outerClassName); // FIXME: WE ACTUALLY DON'T MANAGE INNER CLASS OF INNER CLASS
classMappingMap.put(outerClassName, parent);
mappings.addClassMapping(parent);
}
}
parent.addInnerClassMapping(classMapping);
} else
mappings.addClassMapping(classMapping);
} catch (MappingConflict e) {
throw new MappingParseException(file, -1, e.getMessage());
}
}
lines.clear();
classMappingMap.clear();
return mappings;
}
}
|