-
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
240 additions
and
2 deletions.
There are no files selected for viewing
101 changes: 101 additions & 0 deletions
101
enigma-cli/src/main/java/cuchaz/enigma/command/FillClassMappingsCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package cuchaz.enigma.command; | ||
|
||
import cuchaz.enigma.Enigma; | ||
import cuchaz.enigma.EnigmaProject; | ||
import cuchaz.enigma.ProgressListener; | ||
import cuchaz.enigma.analysis.index.JarIndex; | ||
import cuchaz.enigma.classprovider.ClasspathClassProvider; | ||
import cuchaz.enigma.translation.mapping.EntryMapping; | ||
import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; | ||
import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; | ||
import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
import cuchaz.enigma.translation.representation.entry.ParentedEntry; | ||
import cuchaz.enigma.utils.Utils; | ||
import org.tinylog.Logger; | ||
|
||
import java.nio.file.Path; | ||
import java.util.List; | ||
|
||
public class FillClassMappingsCommand extends Command { | ||
public static final String NAME = "fill-class-mappings"; | ||
|
||
protected FillClassMappingsCommand() { | ||
super(NAME); | ||
} | ||
|
||
@Override | ||
public String getUsage() { | ||
return "<in-jar> <source> <result> <result-format>"; | ||
} | ||
|
||
@Override | ||
public boolean isValidArgument(int length) { | ||
return length == 4; | ||
} | ||
|
||
@Override | ||
public void run(String... args) throws Exception { | ||
Path inJar = getReadablePath(getArg(args, 0, "in-jar", true)); | ||
Path source = getReadablePath(getArg(args, 1, "source", true)); | ||
Path result = getWritablePath(getArg(args, 2, "result", true)); | ||
String resultFormat = getArg(args, 3, "result-format", true); | ||
|
||
run(inJar, source, result, resultFormat); | ||
} | ||
|
||
public static void run(Path jar, Path source, Path result, String resultFormat) throws Exception { | ||
boolean debug = shouldDebug(NAME); | ||
|
||
Logger.info("Reading JAR..."); | ||
Enigma enigma = Enigma.create(); | ||
EnigmaProject project = enigma.openJar(jar, new ClasspathClassProvider(), ProgressListener.none()); | ||
|
||
Logger.info("Reading mappings..."); | ||
MappingSaveParameters saveParameters = enigma.getProfile().getMappingSaveParameters(); | ||
EntryTree<EntryMapping> mappings = readMappings(source, ProgressListener.none(), saveParameters); | ||
project.setMappings(mappings); | ||
|
||
if (debug) { | ||
mappings = new DeltaTrackingTree<>(mappings); | ||
} | ||
|
||
Logger.info("Adding mappings..."); | ||
JarIndex index = project.getJarIndex(); | ||
List<ClassEntry> rootEntries = mappings.getRootNodes().map(EntryTreeNode::getEntry) | ||
.filter(entry -> entry instanceof ClassEntry) | ||
.map(entry -> (ClassEntry) entry) | ||
.toList(); | ||
for (ClassEntry rootEntry : rootEntries) { | ||
// These entries already have a mapping tree node | ||
recursiveAddMappings(mappings, index, rootEntry, false); | ||
} | ||
|
||
Logger.info("Writing mappings..."); | ||
Utils.delete(result); | ||
MappingCommandsUtil.write(mappings, resultFormat, result, saveParameters); | ||
|
||
if (debug) { | ||
writeDebugDelta((DeltaTrackingTree<EntryMapping>) mappings, result); | ||
} | ||
} | ||
|
||
private static void recursiveAddMappings(EntryTree<EntryMapping> mappings, JarIndex index, ClassEntry entry, boolean addMapping) { | ||
EntryTreeNode<EntryMapping> node = mappings.findNode(entry); | ||
boolean hasMapping = node != null && node.hasValue() && node.getValue().targetName() != null; | ||
|
||
Logger.debug("Entry {} {} a mapping", entry, hasMapping ? "has" : "doesn't have"); | ||
if (!hasMapping && addMapping) { | ||
Logger.debug("Adding mapping for {}", entry); | ||
mappings.insert(entry, EntryMapping.DEFAULT); | ||
} | ||
|
||
List<ParentedEntry<?>> children = index.getChildrenByClass().get(entry); | ||
for (ParentedEntry<?> child : children) { | ||
if (child instanceof ClassEntry classChild) { | ||
recursiveAddMappings(mappings, index, classChild, addMapping || hasMapping); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
enigma-cli/src/test/java/cuchaz/enigma/command/FillClassMappingsCommandTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package cuchaz.enigma.command; | ||
|
||
import cuchaz.enigma.ProgressListener; | ||
import cuchaz.enigma.translation.mapping.EntryMapping; | ||
import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; | ||
import cuchaz.enigma.translation.mapping.serde.MappingFormat; | ||
import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; | ||
import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
import cuchaz.enigma.translation.representation.entry.Entry; | ||
import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.net.URISyntaxException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
import static org.junit.jupiter.api.Assertions.assertNull; | ||
|
||
public class FillClassMappingsCommandTest { | ||
private static final Path JAR = Path.of("../enigma/build/test-obf/innerClasses.jar"); | ||
private static final Path MAPPINGS; | ||
|
||
private static final ClassEntry A = new ClassEntry("a"); | ||
private static final MethodEntry A_METHOD = MethodEntry.parse("a", "a", "()V"); | ||
private static final ClassEntry A_ANONYMOUS = new ClassEntry("a$1"); | ||
private static final ClassEntry B = new ClassEntry("b"); | ||
private static final MethodEntry B_METHOD = MethodEntry.parse("b", "a", "(Ld;)V"); | ||
private static final ClassEntry B_ANONYMOUS = new ClassEntry("b$1"); | ||
private static final ClassEntry C = new ClassEntry("c"); | ||
private static final FieldEntry C_FIELD = FieldEntry.parse("c", "a", "c$a"); | ||
private static final ClassEntry C_INNER = new ClassEntry("c$a"); | ||
private static final FieldEntry C_INNER_FIELD = FieldEntry.parse("c$a", "a", "I"); | ||
private static final ClassEntry D = new ClassEntry("d"); | ||
private static final ClassEntry D_INNER = new ClassEntry("d$a"); | ||
private static final ClassEntry E = new ClassEntry("e"); | ||
private static final MethodEntry E_METHOD_1 = MethodEntry.parse("e", "a", "()Ljava/lang/Object;"); | ||
private static final MethodEntry E_METHOD_2 = MethodEntry.parse("e", "a", "()Ljava/lang/String;"); | ||
private static final ClassEntry E_ANONYMOUS = new ClassEntry("e$1"); | ||
private static final ClassEntry F = new ClassEntry("f"); | ||
private static final ClassEntry F_LEVEL_1 = new ClassEntry("f$a"); | ||
private static final FieldEntry F_LEVEL_1_FIELD = FieldEntry.parse("f$a", "a", "I"); | ||
private static final ClassEntry F_LEVEL_2 = new ClassEntry("f$a$a"); | ||
private static final FieldEntry F_LEVEL_2_FIELD = FieldEntry.parse("f$a$a", "a", "I"); | ||
private static final ClassEntry F_LEVEL_3 = new ClassEntry("f$a$a$a"); | ||
private static final FieldEntry F_LEVEL_3_FIELD = FieldEntry.parse("f$a$a$a", "a", "I"); | ||
|
||
@Test | ||
public void test() throws Exception { | ||
Path resultFile = Files.createTempFile("fillClassMappings", ".mappings"); | ||
FillClassMappingsCommand.run(JAR, MAPPINGS, resultFile, MappingFormat.ENIGMA_FILE.name()); | ||
|
||
EntryTree<EntryMapping> result = MappingFormat.ENIGMA_FILE.read(resultFile, ProgressListener.none(), new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF)); | ||
|
||
assertEquals("A_Anonymous", getName(result, A)); | ||
assertNotNull(result.findNode(A_ANONYMOUS)); | ||
assertNull(getName(result, A_METHOD)); | ||
assertNull(getName(result, A_ANONYMOUS)); | ||
|
||
assertNotNull(result.findNode(B)); | ||
assertNull(getName(result, B)); | ||
assertEquals("foo", getName(result, B_METHOD)); | ||
assertNull(result.findNode(B_ANONYMOUS)); | ||
|
||
assertNotNull(result.findNode(C)); | ||
assertNull(getName(result, C)); | ||
assertNull(getName(result, C_FIELD)); | ||
assertNotNull(result.findNode(C_INNER)); | ||
assertNull(getName(result, C_INNER)); | ||
assertEquals("a", getName(result, C_INNER_FIELD)); | ||
|
||
assertNotNull(result.findNode(D)); | ||
assertNull(getName(result, D)); | ||
assertEquals("Inner", getName(result, D_INNER)); | ||
|
||
assertNotNull(result.findNode(E)); | ||
assertNull(getName(result, E)); | ||
assertNull(getName(result, E_METHOD_1)); | ||
assertEquals("outerMethod", getName(result, E_METHOD_2)); | ||
assertNull(result.findNode(E_ANONYMOUS)); | ||
|
||
assertNotNull(result.findNode(F)); | ||
assertNull(getName(result, F)); | ||
assertNotNull(result.findNode(F_LEVEL_1)); | ||
assertNull(getName(result, F_LEVEL_1)); | ||
assertNull(getName(result, F_LEVEL_1_FIELD)); | ||
assertNotNull(result.findNode(F_LEVEL_2)); | ||
assertEquals("Level2", getName(result, F_LEVEL_2)); | ||
assertNull(getName(result, F_LEVEL_2_FIELD)); | ||
assertNotNull(result.findNode(F_LEVEL_3)); | ||
assertNull(getName(result, F_LEVEL_3)); | ||
assertNull(getName(result, F_LEVEL_3_FIELD)); | ||
} | ||
|
||
private static String getName(EntryTree<EntryMapping> mappings, Entry<?> entry) { | ||
if (!mappings.contains(entry)) { | ||
return null; | ||
} | ||
|
||
EntryMapping mapping = mappings.get(entry); | ||
return mapping != null ? mapping.targetName() : null; | ||
} | ||
|
||
static { | ||
try { | ||
MAPPINGS = Path.of(FillClassMappingsCommandTest.class.getResource("/fillClassMappings/").toURI()); | ||
} catch (URISyntaxException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
enigma-cli/src/test/resources/fillClassMappings/A_Anonymous.mapping
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CLASS a A_Anonymous |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
CLASS b | ||
METHOD a foo (Ld;)V |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
CLASS c | ||
CLASS a | ||
FIELD a a I |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
CLASS d | ||
CLASS a Inner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
CLASS e | ||
METHOD a outerMethod ()Ljava/lang/String; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
CLASS f | ||
CLASS a | ||
CLASS a Level2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
writingthread = true | ||
|
||
writerConsole = console | ||
writerConsole.format = {date: HH:mm:ss.SSS} [{level}]: {message} | ||
writerConsole.level = debug |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters