aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.gradle3
-rw-r--r--src/main/java/com/replaymod/gradle/remap/PsiMapper.java87
-rw-r--r--src/main/java/com/replaymod/gradle/remap/PsiUtils.java61
-rw-r--r--src/main/java/com/replaymod/gradle/remap/Transformer.java113
-rw-r--r--src/main/java/com/replaymod/gradle/remap/legacy/LegacyMapping.java118
-rw-r--r--src/main/java/com/replaymod/gradle/remap/legacy/LegacyMappingSetModelFactory.java36
-rw-r--r--src/main/java/com/replaymod/gradle/remap/legacy/LegacyMappingsReader.java44
7 files changed, 328 insertions, 134 deletions
diff --git a/build.gradle b/build.gradle
index 35aa3df..921cf96 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ sourceCompatibility = '1.8'
targetCompatibility = '1.8'
group = 'com.github.replaymod'
-version = '1.0-SNAPSHOT'
+version = 'SNAPSHOT'
repositories {
mavenCentral()
@@ -15,6 +15,7 @@ repositories {
dependencies {
compile 'org.jetbrains.kotlin:kotlin-compiler:1.3.31'
+ compile 'org.cadixdev:lorenz:0.5.0'
}
jar {
diff --git a/src/main/java/com/replaymod/gradle/remap/PsiMapper.java b/src/main/java/com/replaymod/gradle/remap/PsiMapper.java
index 17d0783..c1b2d41 100644
--- a/src/main/java/com/replaymod/gradle/remap/PsiMapper.java
+++ b/src/main/java/com/replaymod/gradle/remap/PsiMapper.java
@@ -18,7 +18,11 @@ import com.intellij.psi.PsiPackage;
import com.intellij.psi.PsiQualifiedNamedElement;
import com.intellij.psi.PsiSwitchLabelStatement;
import com.intellij.psi.PsiTypeElement;
-import com.replaymod.gradle.remap.Transformer.Mapping;
+import org.cadixdev.bombe.type.signature.MethodSignature;
+import org.cadixdev.lorenz.MappingSet;
+import org.cadixdev.lorenz.model.ClassMapping;
+import org.cadixdev.lorenz.model.Mapping;
+import org.cadixdev.lorenz.model.MethodMapping;
import java.util.ArrayDeque;
import java.util.Arrays;
@@ -27,6 +31,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
+import static com.replaymod.gradle.remap.PsiUtils.getSignature;
+
class PsiMapper {
private static final String CLASS_MIXIN = "org.spongepowered.asm.mixin.Mixin";
private static final String CLASS_ACCESSOR = "org.spongepowered.asm.mixin.gen.Accessor";
@@ -34,17 +40,23 @@ class PsiMapper {
private static final String CLASS_INJECT = "org.spongepowered.asm.mixin.injection.Inject";
private static final String CLASS_REDIRECT = "org.spongepowered.asm.mixin.injection.Redirect";
- private final Map<String, Mapping> map;
- private final Map<String, Mapping> mixinMappings = new HashMap<>();
+ private final MappingSet map;
+ private final Map<String, ClassMapping<?, ?>> mixinMappings = new HashMap<>();
private final PsiFile file;
private boolean error;
private TreeMap<TextRange, String> changes = new TreeMap<>(Comparator.comparing(TextRange::getStartOffset));
- PsiMapper(Map<String, Mapping> map, PsiFile file) {
+ PsiMapper(MappingSet map, PsiFile file) {
this.map = map;
this.file = file;
}
+ private void error(PsiElement at, String message) {
+ int line = StringUtil.offsetToLineNumber(file.getText(), at.getTextOffset());
+ System.err.println(file.getName() + ":" + line + ": " + message);
+ error = true;
+ }
+
private void replace(PsiElement e, String with) {
changes.put(e.getTextRange(), with);
}
@@ -87,12 +99,12 @@ class PsiMapper {
if (declaringClass == null) return;
String name = declaringClass.getQualifiedName();
if (name == null) return;
- Mapping mapping = this.mixinMappings.get(name);
+ ClassMapping<?, ?> mapping = this.mixinMappings.get(name);
if (mapping == null) {
- mapping = map.get(name);
+ mapping = map.getClassMapping(name).orElse(null);
}
if (mapping == null) return;
- String mapped = mapping.fields.get(field.getName());
+ String mapped = mapping.getFieldMapping(field.getName()).map(Mapping::getDeobfuscatedName).orElse(null);
if (mapped == null || mapped.equals(field.getName())) return;
replaceIdentifier(expr, mapped);
@@ -100,20 +112,20 @@ class PsiMapper {
&& !((PsiJavaCodeReferenceElement) expr).isQualified() // qualified access is fine
&& !isSwitchCase(expr) // referencing constants in case statements is fine
) {
- int line = StringUtil.offsetToLineNumber(file.getText(), expr.getTextOffset());
- System.err.println(file.getName() + ":" + line + ": Implicit member reference to remapped field \"" + field.getName() + "\". " +
+ error(expr, "Implicit member reference to remapped field \"" + field.getName() + "\". " +
"This can cause issues if the remapped reference becomes shadowed by a local variable and is therefore forbidden. " +
"Use \"this." + field.getName() + "\" instead.");
- error = true;
}
}
private void map(PsiElement expr, PsiMethod method) {
+ if (method.isConstructor()) return;
+
PsiClass declaringClass = method.getContainingClass();
if (declaringClass == null) return;
ArrayDeque<PsiClass> parentQueue = new ArrayDeque<>();
parentQueue.offer(declaringClass);
- Mapping mapping = null;
+ ClassMapping<?, ?> mapping = null;
String name = declaringClass.getQualifiedName();
if (name != null) {
@@ -121,7 +133,7 @@ class PsiMapper {
}
while (true) {
if (mapping != null) {
- String mapped = mapping.methods.get(method.getName());
+ String mapped = mapping.getMethodMapping(getSignature(method)).map(Mapping::getDeobfuscatedName).orElse(null);
if (mapped != null) {
if (!mapped.equals(method.getName())) {
replaceIdentifier(expr, mapped);
@@ -144,7 +156,7 @@ class PsiMapper {
name = declaringClass.getQualifiedName();
if (name == null) continue;
- mapping = map.get(name);
+ mapping = map.getClassMapping(name).orElse(null);
}
}
}
@@ -152,10 +164,11 @@ class PsiMapper {
private void map(PsiElement expr, PsiQualifiedNamedElement resolved) {
String name = resolved.getQualifiedName();
if (name == null) return;
- Mapping mapping = map.get(name);
+ ClassMapping<?, ?> mapping = map.getClassMapping(name).orElse(null);
if (mapping == null) return;
- String mapped = mapping.newName;
+ String mapped = mapping.getDeobfuscatedName();
if (mapped.equals(name)) return;
+ mapped = mapped.replace('/', '.');
if (expr.getText().equals(name)) {
replace(expr, mapped);
@@ -189,7 +202,7 @@ class PsiMapper {
return null;
}
- private void remapAccessors(Mapping mapping) {
+ private void remapAccessors(ClassMapping<?, ?> mapping) {
file.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitMethod(PsiMethod method) {
@@ -218,7 +231,7 @@ class PsiMapper {
throw new IllegalArgumentException("Cannot determine accessor target for " + method);
}
- String mapped = mapping.fields.get(target);
+ String mapped = mapping.getFieldMapping(target).map(Mapping::getDeobfuscatedName).orElse(null);
if (mapped != null && !mapped.equals(target)) {
// Update accessor target
String parameterList;
@@ -235,7 +248,7 @@ class PsiMapper {
});
}
- private void remapInjectsAndRedirects(Mapping mapping) {
+ private void remapInjectsAndRedirects(ClassMapping<?, ?> mapping) {
file.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitMethod(PsiMethod method) {
@@ -250,7 +263,21 @@ class PsiMapper {
// Note: mixin supports multiple targets, we do not (yet)
String literalValue = attribute.getLiteralValue();
if (literalValue == null) continue;
- String mapped = mapping.methods.get(literalValue);
+ String mapped;
+ if (literalValue.contains("(")) {
+ mapped = mapping.getMethodMapping(MethodSignature.of(literalValue)).map(Mapping::getDeobfuscatedName).orElse(null);
+ } else {
+ mapped = null;
+ for (MethodMapping methodMapping : mapping.getMethodMappings()) {
+ if (methodMapping.getObfuscatedName().equals(literalValue)) {
+ String name = methodMapping.getDeobfuscatedName();
+ if (mapped != null && !mapped.equals(name)) {
+ error(attribute, "Ambiguous mixin method \"" + literalValue + "\" maps to \"" + mapped + "\" and \"" + name + "\"");
+ }
+ mapped = name;
+ }
+ }
+ }
if (mapped != null && !mapped.equals(literalValue)) {
PsiAnnotationMemberValue value = attribute.getValue();
assert value != null;
@@ -261,12 +288,12 @@ class PsiMapper {
});
}
- private Mapping remapInternalType(String internalType, StringBuilder result) {
+ private ClassMapping<?, ?> remapInternalType(String internalType, StringBuilder result) {
if (internalType.charAt(0) == 'L') {
String type = internalType.substring(1, internalType.length() - 1).replace('/', '.');
- Mapping mapping = map.get(type);
+ ClassMapping<?, ?> mapping = map.getClassMapping(type).orElse(null);
if (mapping != null) {
- result.append('L').append(mapping.newName.replace('.', '/')).append(';');
+ result.append('L').append(mapping.getFullDeobfuscatedName()).append(';');
return mapping;
}
}
@@ -287,10 +314,14 @@ class PsiMapper {
String returnType = signature.substring(argsEnd + 1);
StringBuilder builder = new StringBuilder(signature.length() + 32);
- Mapping mapping = remapInternalType(owner, builder);
+ ClassMapping<?, ?> mapping = remapInternalType(owner, builder);
String mapped = null;
if (mapping != null) {
- mapped = (method ? mapping.methods : mapping.fields).get(name);
+ mapped = (method
+ ? mapping.getMethodMapping(MethodSignature.of(signature.substring(ownerEnd + 1)))
+ : mapping.getFieldMapping(name))
+ .map(Mapping::getDeobfuscatedName)
+ .orElse(null);
}
builder.append(mapped != null ? mapped : name);
if (method) {
@@ -350,16 +381,18 @@ class PsiMapper {
PsiClass target = getMixinTarget(annotation);
if (target == null) return;
+ String qualifiedName = target.getQualifiedName();
+ if (qualifiedName == null) return;
- Mapping mapping = map.get(target.getQualifiedName());
+ ClassMapping<?, ?> mapping = map.getClassMapping(qualifiedName).orElse(null);
if (mapping == null) return;
mixinMappings.put(psiClass.getQualifiedName(), mapping);
- if (!mapping.fields.isEmpty()) {
+ if (!mapping.getFieldMappings().isEmpty()) {
remapAccessors(mapping);
}
- if (!mapping.methods.isEmpty()) {
+ if (!mapping.getMethodMappings().isEmpty()) {
remapInjectsAndRedirects(mapping);
}
}
diff --git a/src/main/java/com/replaymod/gradle/remap/PsiUtils.java b/src/main/java/com/replaymod/gradle/remap/PsiUtils.java
new file mode 100644
index 0000000..ec4d1c8
--- /dev/null
+++ b/src/main/java/com/replaymod/gradle/remap/PsiUtils.java
@@ -0,0 +1,61 @@
+package com.replaymod.gradle.remap;
+
+import com.intellij.psi.PsiArrayType;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.util.TypeConversionUtil;
+import org.cadixdev.bombe.type.ArrayType;
+import org.cadixdev.bombe.type.FieldType;
+import org.cadixdev.bombe.type.MethodDescriptor;
+import org.cadixdev.bombe.type.ObjectType;
+import org.cadixdev.bombe.type.Type;
+import org.cadixdev.bombe.type.VoidType;
+import org.cadixdev.bombe.type.signature.MethodSignature;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+class PsiUtils {
+ static MethodSignature getSignature(PsiMethod method) {
+ return new MethodSignature(method.getName(), getDescriptor(method));
+ }
+
+ private static MethodDescriptor getDescriptor(PsiMethod method) {
+ return new MethodDescriptor(
+ Arrays.stream(method.getParameterList().getParameters())
+ .map(it -> getFieldType(it.getType()))
+ .collect(Collectors.toList()),
+ getType(method.getReturnType())
+ );
+ }
+
+ private static FieldType getFieldType(PsiType type) {
+ type = TypeConversionUtil.erasure(type);
+ if (type instanceof PsiPrimitiveType) {
+ return FieldType.of(((PsiPrimitiveType) type).getKind().getBinaryName());
+ } else if (type instanceof PsiArrayType) {
+ PsiArrayType array = (PsiArrayType) type;
+ return new ArrayType(array.getArrayDimensions(), getFieldType(array.getDeepComponentType()));
+ } else if (type instanceof PsiClassType) {
+ PsiClass resolved = ((PsiClassType) type).resolve();
+ if (resolved == null) throw new NullPointerException("Failed to resolve type " + type);
+ String qualifiedName = resolved.getQualifiedName();
+ if (qualifiedName == null) throw new NullPointerException("Type " + type + " has no qualified name.");
+ return new ObjectType(qualifiedName);
+ } else {
+ throw new IllegalArgumentException("Cannot translate type " + type);
+ }
+ }
+
+ private static Type getType(PsiType type) {
+ if (TypeConversionUtil.isVoidType(type)) {
+ return VoidType.INSTANCE;
+ } else {
+ return getFieldType(type);
+ }
+ }
+
+}
diff --git a/src/main/java/com/replaymod/gradle/remap/Transformer.java b/src/main/java/com/replaymod/gradle/remap/Transformer.java
index 1cbd425..8a9ec9c 100644
--- a/src/main/java/com/replaymod/gradle/remap/Transformer.java
+++ b/src/main/java/com/replaymod/gradle/remap/Transformer.java
@@ -11,6 +11,8 @@ import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.local.CoreLocalFileSystem;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
+import com.replaymod.gradle.remap.legacy.LegacyMapping;
+import org.cadixdev.lorenz.MappingSet;
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot;
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer;
@@ -37,21 +39,20 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class Transformer {
- private Map<String, Mapping> map;
+ private MappingSet map;
private String[] classpath;
private boolean fail;
public static void main(String[] args) throws IOException {
- Map<String, Mapping> mappings;
+ MappingSet mappings;
if (args[0].isEmpty()) {
- mappings = new HashMap<>();
+ mappings = MappingSet.create();
} else {
- mappings = readMappings(new File(args[0]), args[1].equals("true"));
+ mappings = LegacyMapping.readMappingSet(new File(args[0]).toPath(), args[1].equals("true"));
}
Transformer transformer = new Transformer(mappings);
@@ -95,7 +96,7 @@ class Transformer {
}
}
- public Transformer(Map<String, Mapping> mappings) {
+ public Transformer(MappingSet mappings) {
this.map = mappings;
}
@@ -165,104 +166,4 @@ class Transformer {
}
}
- public static class Mapping {
- public String oldName;
- public String newName;
- public Map<String, String> fields = new HashMap<>();
- public Map<String, String> methods = new HashMap<>();
- }
-
- public static Map<String, Mapping> readMappings(File mappingFile, boolean invert) throws IOException {
- Map<String, Mapping> mappings = new HashMap<>();
- Map<String, Mapping> revMappings = new HashMap<>();
- int lineNumber = 0;
- for (String line : Files.readAllLines(mappingFile.toPath(), StandardCharsets.UTF_8)) {
- lineNumber++;
- if (line.trim().startsWith("#") || line.trim().isEmpty()) continue;
-
- String[] parts = line.split(" ");
- if (parts.length < 2 || line.contains(";")) {
- throw new IllegalArgumentException("Failed to parse line " + lineNumber + " in " + mappingFile.getPath() + ".");
- }
-
- Mapping mapping = mappings.get(parts[0]);
- if (mapping == null) {
- mapping = new Mapping();
- mapping.oldName = mapping.newName = parts[0];
- mappings.put(mapping.oldName, mapping);
- }
-
- if (parts.length == 2) {
- // Class mapping
- mapping.newName = parts[1];
- // Possibly merge with reverse mapping
- Mapping revMapping = revMappings.remove(mapping.newName);
- if (revMapping != null) {
- mapping.fields.putAll(revMapping.fields);
- mapping.methods.putAll(revMapping.methods);
- }
- revMappings.put(mapping.newName, mapping);
- } else if (parts.length == 3 || parts.length == 4) {
- String fromName = parts[1];
- String toName;
- Mapping revMapping;
- if (parts.length == 4) {
- toName = parts[3];
- revMapping = revMappings.get(parts[2]);
- if (revMapping == null) {
- revMapping = new Mapping();
- revMapping.oldName = revMapping.newName = parts[2];
- revMappings.put(revMapping.newName, revMapping);
- }
- } else {
- toName = parts[2];
- revMapping = mapping;
- }
- if (fromName.endsWith("()")) {
- // Method mapping
- fromName = fromName.substring(0, fromName.length() - 2);
- toName = toName.substring(0, toName.length() - 2);
- mapping.methods.put(fromName, toName);
- revMapping.methods.put(fromName, toName);
- } else {
- // Field mapping
- mapping.fields.put(fromName, toName);
- revMapping.fields.put(fromName, toName);
- }
- } else {
- throw new IllegalArgumentException("Failed to parse line " + lineNumber + " in " + mappingFile.getPath() + ".");
- }
- }
- if (invert) {
- Stream.concat(
- mappings.values().stream(),
- revMappings.values().stream()
- ).distinct().forEach(it -> {
- String oldName = it.oldName;
- it.oldName = it.newName;
- it.newName = oldName;
- it.fields = it.fields.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey));
- it.methods = it.methods.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey));
- });
- }
- return Stream.concat(
- mappings.values().stream(),
- revMappings.values().stream()
- ).collect(Collectors.toMap(mapping -> mapping.oldName, Function.identity(), (mapping, other) -> {
- if (!other.oldName.equals(other.newName)) {
- if (!mapping.oldName.equals(mapping.newName)
- && !other.oldName.equals(mapping.oldName)
- && !other.newName.equals(mapping.newName)) {
- throw new IllegalArgumentException("Conflicting mappings: "
- + mapping.oldName + " -> " + mapping.newName
- + " and " + other.oldName + " -> " + other.newName);
- }
- mapping.oldName = other.oldName;
- mapping.newName = other.newName;
- }
- mapping.fields.putAll(other.fields);
- mapping.methods.putAll(other.methods);
- return mapping;
- }));
- }
}
diff --git a/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMapping.java b/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMapping.java
new file mode 100644
index 0000000..05a85d9
--- /dev/null
+++ b/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMapping.java
@@ -0,0 +1,118 @@
+package com.replaymod.gradle.remap.legacy;
+
+import org.cadixdev.lorenz.MappingSet;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class LegacyMapping {
+ public static MappingSet readMappingSet(Path mappingFile, boolean invert) throws IOException {
+ return new LegacyMappingsReader(readMappings(mappingFile, invert)).read();
+ }
+
+ public static Map<String, LegacyMapping> readMappings(Path mappingFile, boolean invert) throws IOException {
+ Map<String, LegacyMapping> mappings = new HashMap<>();
+ Map<String, LegacyMapping> revMappings = new HashMap<>();
+ int lineNumber = 0;
+ for (String line : Files.readAllLines(mappingFile, StandardCharsets.UTF_8)) {
+ lineNumber++;
+ if (line.trim().startsWith("#") || line.trim().isEmpty()) continue;
+
+ String[] parts = line.split(" ");
+ if (parts.length < 2 || line.contains(";")) {
+ throw new IllegalArgumentException("Failed to parse line " + lineNumber + " in " + mappingFile + ".");
+ }
+
+ LegacyMapping mapping = mappings.get(parts[0]);
+ if (mapping == null) {
+ mapping = new LegacyMapping();
+ mapping.oldName = mapping.newName = parts[0];
+ mappings.put(mapping.oldName, mapping);
+ }
+
+ if (parts.length == 2) {
+ // Class mapping
+ mapping.newName = parts[1];
+ // Possibly merge with reverse mapping
+ LegacyMapping revMapping = revMappings.remove(mapping.newName);
+ if (revMapping != null) {
+ mapping.fields.putAll(revMapping.fields);
+ mapping.methods.putAll(revMapping.methods);
+ }
+ revMappings.put(mapping.newName, mapping);
+ } else if (parts.length == 3 || parts.length == 4) {
+ String fromName = parts[1];
+ String toName;
+ LegacyMapping revMapping;
+ if (parts.length == 4) {
+ toName = parts[3];
+ revMapping = revMappings.get(parts[2]);
+ if (revMapping == null) {
+ revMapping = new LegacyMapping();
+ revMapping.oldName = revMapping.newName = parts[2];
+ revMappings.put(revMapping.newName, revMapping);
+ }
+ } else {
+ toName = parts[2];
+ revMapping = mapping;
+ }
+ if (fromName.endsWith("()")) {
+ // Method mapping
+ fromName = fromName.substring(0, fromName.length() - 2);
+ toName = toName.substring(0, toName.length() - 2);
+ mapping.methods.put(fromName, toName);
+ revMapping.methods.put(fromName, toName);
+ } else {
+ // Field mapping
+ mapping.fields.put(fromName, toName);
+ revMapping.fields.put(fromName, toName);
+ }
+ } else {
+ throw new IllegalArgumentException("Failed to parse line " + lineNumber + " in " + mappingFile + ".");
+ }
+ }
+ if (invert) {
+ Stream.concat(
+ mappings.values().stream(),
+ revMappings.values().stream()
+ ).distinct().forEach(it -> {
+ String oldName = it.oldName;
+ it.oldName = it.newName;
+ it.newName = oldName;
+ it.fields = it.fields.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
+ it.methods = it.methods.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
+ });
+ }
+ return Stream.concat(
+ mappings.values().stream(),
+ revMappings.values().stream()
+ ).collect(Collectors.toMap(mapping -> mapping.oldName, Function.identity(), (mapping, other) -> {
+ if (!other.oldName.equals(other.newName)) {
+ if (!mapping.oldName.equals(mapping.newName)
+ && !other.oldName.equals(mapping.oldName)
+ && !other.newName.equals(mapping.newName)) {
+ throw new IllegalArgumentException("Conflicting mappings: "
+ + mapping.oldName + " -> " + mapping.newName
+ + " and " + other.oldName + " -> " + other.newName);
+ }
+ mapping.oldName = other.oldName;
+ mapping.newName = other.newName;
+ }
+ mapping.fields.putAll(other.fields);
+ mapping.methods.putAll(other.methods);
+ return mapping;
+ }));
+ }
+
+ public String oldName;
+ public String newName;
+ public Map<String, String> fields = new HashMap<>();
+ public Map<String, String> methods = new HashMap<>();
+}
diff --git a/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMappingSetModelFactory.java b/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMappingSetModelFactory.java
new file mode 100644
index 0000000..eb72788
--- /dev/null
+++ b/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMappingSetModelFactory.java
@@ -0,0 +1,36 @@
+package com.replaymod.gradle.remap.legacy;
+
+import org.cadixdev.bombe.type.signature.MethodSignature;
+import org.cadixdev.lorenz.MappingSet;
+import org.cadixdev.lorenz.impl.MappingSetModelFactoryImpl;
+import org.cadixdev.lorenz.impl.model.TopLevelClassMappingImpl;
+import org.cadixdev.lorenz.model.MethodMapping;
+import org.cadixdev.lorenz.model.TopLevelClassMapping;
+
+import java.util.Optional;
+
+public class LegacyMappingSetModelFactory extends MappingSetModelFactoryImpl {
+ @Override
+ public TopLevelClassMapping createTopLevelClassMapping(MappingSet parent, String obfuscatedName, String deobfuscatedName) {
+ return new TopLevelClassMappingImpl(parent, obfuscatedName, deobfuscatedName) {
+ private MethodSignature stripDesc(MethodSignature signature) {
+ // actual descriptor isn't included in legacy format
+ return MethodSignature.of(signature.getName(), "()V");
+ }
+
+ @Override
+ public boolean hasMethodMapping(MethodSignature signature) {
+ return super.hasMethodMapping(signature) || super.hasMethodMapping(stripDesc(signature));
+ }
+
+ @Override
+ public Optional<MethodMapping> getMethodMapping(MethodSignature signature) {
+ Optional<MethodMapping> maybeMapping = super.getMethodMapping(signature);
+ if (!maybeMapping.isPresent() || !maybeMapping.get().hasMappings()) {
+ maybeMapping = super.getMethodMapping(stripDesc(signature));
+ }
+ return maybeMapping;
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMappingsReader.java b/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMappingsReader.java
new file mode 100644
index 0000000..502f7ba
--- /dev/null
+++ b/src/main/java/com/replaymod/gradle/remap/legacy/LegacyMappingsReader.java
@@ -0,0 +1,44 @@
+package com.replaymod.gradle.remap.legacy;
+
+import org.cadixdev.lorenz.MappingSet;
+import org.cadixdev.lorenz.io.MappingsReader;
+import org.cadixdev.lorenz.model.ClassMapping;
+
+import java.util.Map;
+
+public class LegacyMappingsReader extends MappingsReader {
+ private final Map<String, LegacyMapping> map;
+
+ public LegacyMappingsReader(Map<String, LegacyMapping> map) {
+ this.map = map;
+ }
+
+ @Override
+ public MappingSet read() {
+ return read(MappingSet.create(new LegacyMappingSetModelFactory()));
+ }
+
+ @Override
+ public MappingSet read(MappingSet mappings) {
+ if (!(mappings.getModelFactory() instanceof LegacyMappingSetModelFactory)) {
+ throw new IllegalArgumentException("legacy mappings must use legacy model factory, use read() instead");
+ }
+ for (LegacyMapping legacyMapping : map.values()) {
+ ClassMapping classMapping = mappings.getOrCreateClassMapping(legacyMapping.oldName)
+ .setDeobfuscatedName(legacyMapping.newName);
+ for (Map.Entry<String, String> entry : legacyMapping.fields.entrySet()) {
+ classMapping.getOrCreateFieldMapping(entry.getKey())
+ .setDeobfuscatedName(entry.getValue());
+ }
+ for (Map.Entry<String, String> entry : legacyMapping.methods.entrySet()) {
+ classMapping.getOrCreateMethodMapping(entry.getKey(), "()V")
+ .setDeobfuscatedName(entry.getValue());
+ }
+ }
+ return mappings;
+ }
+
+ @Override
+ public void close() {
+ }
+}