Skip to content

Commit

Permalink
Mapping System Overhaul
Browse files Browse the repository at this point in the history
Close #121
Fix incorrect tiny v1 reading
Further docs about the new api design will be written on wiki later
  • Loading branch information
XiaoPangxie732 committed Feb 13, 2024
1 parent 4932543 commit 652f3f3
Show file tree
Hide file tree
Showing 32 changed files with 942 additions and 789 deletions.
31 changes: 16 additions & 15 deletions src/main/java/cn/maxpixel/mcdecompiler/ClassifiedDeobfuscator.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import cn.maxpixel.mcdecompiler.mapping.NamespacedMapping;
import cn.maxpixel.mcdecompiler.mapping.PairedMapping;
import cn.maxpixel.mcdecompiler.mapping.collection.ClassMapping;
import cn.maxpixel.mcdecompiler.mapping.collection.ClassifiedMapping;
import cn.maxpixel.mcdecompiler.mapping.type.MappingTypes;
import cn.maxpixel.mcdecompiler.reader.ClassifiedMappingReader;
import cn.maxpixel.mcdecompiler.util.*;
Expand Down Expand Up @@ -79,32 +80,32 @@ public ClassifiedDeobfuscator(String version, Info.SideType side) {
}

public ClassifiedDeobfuscator(String version, Info.SideType side, DeobfuscateOptions options) {
this(new ClassifiedMappingReader<>(MappingTypes.PROGUARD, DownloadingUtil.downloadMappingSync(version, side)), options);
this(new ClassifiedMappingReader<>(MappingTypes.PROGUARD).read(DownloadingUtil.downloadMappingSync(version, side)), options);
}

public ClassifiedDeobfuscator(ClassifiedMappingReader<PairedMapping> reader) {
this(reader, DEFAULT_OPTIONS);
public ClassifiedDeobfuscator(ClassifiedMapping<PairedMapping> mappings) {
this(mappings, DEFAULT_OPTIONS);
}

public ClassifiedDeobfuscator(ClassifiedMappingReader<PairedMapping> reader, DeobfuscateOptions options) {
public ClassifiedDeobfuscator(ClassifiedMapping<PairedMapping> mappings, DeobfuscateOptions options) {
this.options = Objects.requireNonNull(options);
this.targetNamespace = null;
if(options.reverse()) ClassifiedMappingReader.reverse(Objects.requireNonNull(reader));
this.mappings = ClassMapping.genMappingsByUnmappedNameMap(reader.mappings);
this.mappingRemapper = new ClassifiedMappingRemapper(reader.mappings);
if (options.reverse()) mappings.reverse();
this.mappings = ClassifiedMappingRemapper.genMappingsByUnmappedNameMap(mappings.classes);
this.mappingRemapper = new ClassifiedMappingRemapper(mappings);
}

public ClassifiedDeobfuscator(ClassifiedMappingReader<NamespacedMapping> reader, String targetNamespace) {
this(reader, targetNamespace, DEFAULT_OPTIONS);
public ClassifiedDeobfuscator(ClassifiedMapping<NamespacedMapping> mappings, String targetNamespace) {
this(mappings, targetNamespace, DEFAULT_OPTIONS);
}

public ClassifiedDeobfuscator(ClassifiedMappingReader<NamespacedMapping> reader, String targetNamespace, DeobfuscateOptions options) {
public ClassifiedDeobfuscator(ClassifiedMapping<NamespacedMapping> mappings, String targetNamespace, DeobfuscateOptions options) {
this.options = Objects.requireNonNull(options);
String sourceNamespace = NamingUtil.findSourceNamespace(Objects.requireNonNull(reader).mappings);
this.targetNamespace = Objects.requireNonNull(targetNamespace);
if(options.reverse()) ClassifiedMappingReader.swap(reader, sourceNamespace, targetNamespace);
this.mappings = ClassMapping.genMappingsByNamespaceMap(reader.mappings, sourceNamespace);
this.mappingRemapper = new ClassifiedMappingRemapper(reader.mappings, sourceNamespace, targetNamespace);
String sourceNamespace = NamingUtil.getSourceNamespace(mappings);
this.targetNamespace = targetNamespace == null ? NamingUtil.inferTargetNamespace(mappings) : targetNamespace;
if (options.reverse()) mappings.swap(sourceNamespace, this.targetNamespace);
this.mappings = ClassifiedMappingRemapper.genMappingsByNamespaceMap(mappings.classes, sourceNamespace);
this.mappingRemapper = new ClassifiedMappingRemapper(mappings, this.targetNamespace);
}

final ObjectOpenHashSet<String> toDecompile = new ObjectOpenHashSet<>();
Expand Down
32 changes: 16 additions & 16 deletions src/main/java/cn/maxpixel/mcdecompiler/MinecraftDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
import cn.maxpixel.mcdecompiler.decompiler.ILibRecommendedDecompiler;
import cn.maxpixel.mcdecompiler.mapping.NamespacedMapping;
import cn.maxpixel.mcdecompiler.mapping.PairedMapping;
import cn.maxpixel.mcdecompiler.reader.AbstractMappingReader;
import cn.maxpixel.mcdecompiler.reader.ClassifiedMappingReader;
import cn.maxpixel.mcdecompiler.mapping.collection.ClassifiedMapping;
import cn.maxpixel.mcdecompiler.mapping.collection.MappingCollection;
import cn.maxpixel.mcdecompiler.mapping.trait.NamespacedTrait;
import cn.maxpixel.mcdecompiler.util.*;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
Expand Down Expand Up @@ -174,7 +175,7 @@ public static final class OptionBuilder {
private Info.SideType type;
private boolean includeOthers = true;
private boolean rvn;
private AbstractMappingReader<?, ?, ?> mappingReader;
private MappingCollection<?> mappingCollection;
private Path outputJar;
private Path outputDecompDir;
private final ObjectSet<Path> extraJars = new ObjectOpenHashSet<>();
Expand Down Expand Up @@ -288,8 +289,8 @@ public OptionBuilder libsUsing(String version) {
return this;
}

public OptionBuilder withMapping(AbstractMappingReader<?, ?, ?> mappingReader) {
this.mappingReader = Objects.requireNonNull(mappingReader, "mappingReader cannot be null");
public OptionBuilder withMapping(MappingCollection<?> mappingCollection) {
this.mappingCollection = Objects.requireNonNull(mappingCollection, "mappingCollection cannot be null");
return this;
}

Expand All @@ -303,8 +304,8 @@ public OptionBuilder outputDecomp(Path outputDecompDir) {
return this;
}

public OptionBuilder targetNamespace(String targetNamespace) {
this.targetNamespace = Objects.requireNonNull(targetNamespace, "targetNamespace cannot be null");
public OptionBuilder targetNamespace(@Nullable String targetNamespace) {
this.targetNamespace = targetNamespace;
return this;
}

Expand Down Expand Up @@ -376,8 +377,8 @@ public boolean rvn() {
}

@Override
public AbstractMappingReader<?, ?, ?> mappingReader() {
return mappingReader;
public MappingCollection<?> mappings() {
return mappingCollection;
}

@Override
Expand Down Expand Up @@ -435,12 +436,11 @@ private interface Options extends ClassifiedDeobfuscator.DeobfuscateOptions {

@SuppressWarnings("unchecked")
private ClassifiedDeobfuscator buildDeobfuscator() {
if(mappingReader() != null) {
if(mappingReader() instanceof ClassifiedMappingReader<?> reader) {
if(reader.isNamespaced()) {
return new ClassifiedDeobfuscator((ClassifiedMappingReader<NamespacedMapping>) reader,
Objects.requireNonNull(targetNamespace(), "You are using a namespaced mapping but no target namespace is specified"), this);
} else return new ClassifiedDeobfuscator((ClassifiedMappingReader<PairedMapping>) reader, this);
if (mappings() != null) {
if (mappings() instanceof ClassifiedMapping<?> mappings) {
if (mappings.hasTrait(NamespacedTrait.class)) {
return new ClassifiedDeobfuscator((ClassifiedMapping<NamespacedMapping>) mappings, targetNamespace(), this);
} else return new ClassifiedDeobfuscator((ClassifiedMapping<PairedMapping>) mappings, this);
} else throw new UnsupportedOperationException("Unsupported yet"); // TODO
}
return new ClassifiedDeobfuscator(version(), type(), this);
Expand All @@ -452,7 +452,7 @@ private ClassifiedDeobfuscator buildDeobfuscator() {
@Override
boolean rvn();

AbstractMappingReader<?, ?, ?> mappingReader();
MappingCollection<?> mappings();

Path inputJar();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ public static void main(String[] args) throws Throwable {
if(options.has(sideTypeO)) {
builder = new MinecraftDecompiler.OptionBuilder(options.valueOf(versionO), options.valueOf(sideTypeO));
options.valueOfOptional(mappingPathO).ifPresent(LambdaUtil.unwrapConsumer(m -> builder
.withMapping(new ClassifiedMappingReader<>(Utils.tryIdentifyingMappingType(m), m))));
.withMapping(new ClassifiedMappingReader<>(Utils.tryIdentifyingMappingType(m)).read(m))));
} else {
builder = new MinecraftDecompiler.OptionBuilder(options.valueOf(inputO), options.has(reverseO));
String mappingPath = options.valueOf(mappingPathO);
builder.withMapping(new ClassifiedMappingReader<>(Utils.tryIdentifyingMappingType(mappingPath), mappingPath));
builder.withMapping(new ClassifiedMappingReader<>(Utils.tryIdentifyingMappingType(mappingPath)).read(mappingPath));
options.valueOfOptional(versionO).ifPresent(builder::libsUsing);
}
if(options.has(regenVarNameO)) builder.regenerateVariableNames();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public static ClassVisitor getVisitor(ClassWriter writer, ClassifiedDeobfuscator
return cv;
}

@ApiStatus.OverrideOnly
public interface Process {
enum State {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@
import cn.maxpixel.mcdecompiler.mapping.NamespacedMapping;
import cn.maxpixel.mcdecompiler.mapping.PairedMapping;
import cn.maxpixel.mcdecompiler.mapping.collection.ClassMapping;
import cn.maxpixel.mcdecompiler.mapping.collection.ClassifiedMapping;
import cn.maxpixel.mcdecompiler.mapping.component.Descriptor;
import cn.maxpixel.mcdecompiler.util.*;
import cn.maxpixel.mcdecompiler.mapping.trait.NamespacedTrait;
import cn.maxpixel.mcdecompiler.util.DescriptorUtil;
import cn.maxpixel.mcdecompiler.util.Logging;
import cn.maxpixel.mcdecompiler.util.MappingUtil;
import cn.maxpixel.mcdecompiler.util.Utils;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectIterators;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.intellij.lang.annotations.Pattern;
import org.intellij.lang.annotations.Subst;
import org.jetbrains.annotations.NotNull;
Expand All @@ -36,8 +40,10 @@
import org.objectweb.asm.commons.Remapper;

import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.logging.Logger;
import java.util.stream.Collectors;

Expand All @@ -50,11 +56,11 @@ public class ClassifiedMappingRemapper extends Remapper {
private final Object2ObjectOpenHashMap<String, ClassMapping<PairedMapping>> mappingByUnm;
private final Object2ObjectOpenHashMap<String, ClassMapping<PairedMapping>> mappingByMap;

public ClassifiedMappingRemapper(ObjectList<ClassMapping<PairedMapping>> mappings) {
this.fieldByUnm = ClassMapping.genFieldsByUnmappedNameMap(mappings);
this.mappingByUnm = ClassMapping.genMappingsByUnmappedNameMap(mappings);
this.mappingByMap = ClassMapping.genMappingsByMappedNameMap(mappings);
this.methodsByUnm = mappings.parallelStream().collect(Collectors.toMap(cm -> cm.mapping.unmappedName, cm -> {
public ClassifiedMappingRemapper(ClassifiedMapping<PairedMapping> mappings) {
this.fieldByUnm = genFieldsByUnmappedNameMap(mappings.classes);
this.mappingByUnm = genMappingsByUnmappedNameMap(mappings.classes);
this.mappingByMap = genMappingsByMappedNameMap(mappings.classes);
this.methodsByUnm = mappings.classes.parallelStream().collect(Collectors.toMap(cm -> cm.mapping.unmappedName, cm -> {
Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, PairedMapping>> map =
new Object2ObjectOpenHashMap<>();
cm.getMethods().forEach(mm -> {
Expand All @@ -68,30 +74,8 @@ public ClassifiedMappingRemapper(ObjectList<ClassMapping<PairedMapping>> mapping
}, Utils::onKeyDuplicate, Object2ObjectOpenHashMap::new));
}

public ClassifiedMappingRemapper(ObjectList<ClassMapping<NamespacedMapping>> mappings, String targetNamespace) {
this(mappings, NamingUtil.findSourceNamespace(mappings), targetNamespace);
}

public ClassifiedMappingRemapper(ObjectList<ClassMapping<NamespacedMapping>> mappings, String sourceNamespace, String targetNamespace) {
this(mappings.parallelStream()
.map(old -> {
if (!old.mapping.contains(targetNamespace)) {
var availableNamespaces = new ObjectOpenHashSet<>(old.mapping.getNamespaces());
availableNamespaces.remove(sourceNamespace);
throw new IllegalArgumentException(String.format("Target namespace \"%s\" does not exist. Available namespaces: %s",
targetNamespace, availableNamespaces));
}
ClassMapping<PairedMapping> cm = new ClassMapping<>(new PairedMapping(old.mapping.getName(sourceNamespace),
old.mapping.getName(targetNamespace)));
old.getFields().forEach(m -> cm.addField(MappingUtil.Paired.o(m.getName(sourceNamespace), m.getName(targetNamespace))));
old.getMethods().forEach(m -> {
if(!m.getComponent(Descriptor.Namespaced.class).getDescriptorNamespace().equals(sourceNamespace))
throw new IllegalArgumentException();
cm.addMethod(MappingUtil.Paired.duo(m.getName(sourceNamespace), m.getName(targetNamespace),
m.getComponent(Descriptor.Namespaced.class).getUnmappedDescriptor()));
});
return cm;
}).collect(ObjectArrayList.toList()));
public ClassifiedMappingRemapper(ClassifiedMapping<NamespacedMapping> mappings, String targetNamespace) {
this(toPaired(mappings, targetNamespace));
}

public ClassifiedMappingRemapper setExtraClassesInformation(ExtraClassesInformation extraClassesInformation) {
Expand Down Expand Up @@ -275,4 +259,61 @@ else if(Modifier.isPrivate(leftAcc) || Modifier.isPrivate(rightAcc))
throw new IllegalArgumentException("This can't happen!");
throw new IllegalArgumentException("Field duplicated... This should not happen!");
}

public static Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, PairedMapping>> genFieldsByUnmappedNameMap(
ObjectList<ClassMapping<PairedMapping>> mapping) {
return mapping.parallelStream().collect(Collectors.toMap(
cm -> cm.mapping.unmappedName,
cm -> cm.getFields().parallelStream().collect(Collectors.toMap(m -> m.unmappedName, Function.identity(),
Utils::onKeyDuplicate, Object2ObjectOpenHashMap::new)),
Utils::onKeyDuplicate, Object2ObjectOpenHashMap::new));
}

public static Object2ObjectOpenHashMap<String, ClassMapping<PairedMapping>> genMappingsByUnmappedNameMap(
ObjectList<ClassMapping<PairedMapping>> mapping) {
return mapping.parallelStream().collect(Collectors.toMap(cm -> cm.mapping.unmappedName,
Function.identity(), Utils::onKeyDuplicate, Object2ObjectOpenHashMap::new));
}

public static Object2ObjectOpenHashMap<String, ClassMapping<PairedMapping>> genMappingsByMappedNameMap(
ObjectList<ClassMapping<PairedMapping>> mapping) {
return mapping.parallelStream().collect(Collectors.toMap(cm -> cm.mapping.mappedName,
Function.identity(), Utils::onKeyDuplicate, Object2ObjectOpenHashMap::new));
}

public static Object2ObjectOpenHashMap<String, ClassMapping<NamespacedMapping>> genMappingsByNamespaceMap(
ObjectList<ClassMapping<NamespacedMapping>> mapping, String namespace) {
return mapping.parallelStream().collect(Collectors.toMap(m -> m.mapping.getName(namespace),
Function.identity(), Utils::onKeyDuplicate, Object2ObjectOpenHashMap::new));
}

private static ClassifiedMapping<PairedMapping> toPaired(ClassifiedMapping<NamespacedMapping> mappings, String targetNamespace) {
ClassifiedMapping<PairedMapping> paired = new ClassifiedMapping<>();
paired.classes.ensureCapacity(mappings.classes.size());
var namespaces = mappings.getTrait(NamespacedTrait.class).namespaces;
String sourceNamespace = namespaces.first();
if (!namespaces.contains(targetNamespace)) {
var it = namespaces.iterator();
it.next();
throw new IllegalArgumentException(String.format("Target namespace \"%s\" does not exist. Available namespaces: %s",
targetNamespace, Arrays.toString(ObjectIterators.unwrap(it))));
}
mappings.classes.parallelStream().forEach(old -> {
ClassMapping<PairedMapping> cm = new ClassMapping<>(new PairedMapping(old.mapping.getName(sourceNamespace),
old.mapping.getName(targetNamespace)));
for (NamespacedMapping f : old.getFields()) {
cm.addField(MappingUtil.Paired.o(f.getName(sourceNamespace), f.getName(targetNamespace)));
}
for (NamespacedMapping m : old.getMethods()) {
var desc = m.getComponent(Descriptor.Namespaced.class);
if (!sourceNamespace.equals(desc.descriptorNamespace)) throw new IllegalArgumentException();
cm.addMethod(MappingUtil.Paired.duo(m.getName(sourceNamespace), m.getName(targetNamespace),
desc.unmappedDescriptor));
}
synchronized (paired.classes) {
paired.classes.add(cm);
}
});
return paired;
}
}
19 changes: 18 additions & 1 deletion src/main/java/cn/maxpixel/mcdecompiler/mapping/Mapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
import it.unimi.dsi.fastutil.objects.ObjectCollections;
import org.jetbrains.annotations.NotNull;

import java.util.Optional;

/**
* Base class of all mappings
* <p>This class should only be extended, so it is abstract</p>
*
* @implNote This class should only be extended, so it is abstract
*/
public abstract class Mapping implements NameGetter {
private final Object2ObjectOpenHashMap<Class<? extends Component>, Component> components = new Object2ObjectOpenHashMap<>();
Expand All @@ -50,6 +53,7 @@ protected Mapping() {}
/**
* Gets the component of given type if it is present.<br>
* For the {@link Owned} component, it is recommended to use {@link #getOwned()} instead of this method
*
* @param component Given component type. Cannot be null
* @return The component if exists, or {@code null}
*/
Expand All @@ -58,8 +62,21 @@ public final <C extends Component> C getComponent(@NotNull Class<? super C> comp
return (C) components.get(component);
}

/**
* Gets the component of given type.
* <p>
* For the {@link Owned} component, it is recommended to use {@link #getOwned()} instead of this method
*
* @param component Given component type. Cannot be null
* @return The component
*/
public final <C extends Component> Optional<C> getComponentOptional(@NotNull Class<? super C> component) {
return Optional.ofNullable(getComponent(component));
}

/**
* Gets the {@link Owned} component if it is present
*
* @return The {@link Owned} component if it exists, or null
*/
protected Owned<? extends Mapping> getOwned() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@

import org.jetbrains.annotations.NotNull;

/**
* Intended to be a universal interface for getting names
*/
public interface NameGetter {
String getUnmappedName();

String getMappedName();

/**
* For {@link NamespacedMapping} to implement.
*
* @implNote unmapped namespace should be set by mapping processors
*/
interface Namespaced {
String getUnmappedNamespace();

Expand Down
Loading

0 comments on commit 652f3f3

Please sign in to comment.