From f829582ae418504ff6685eeb14fad5a67916c6f9 Mon Sep 17 00:00:00 2001 From: Thog Date: Fri, 24 Mar 2017 01:28:34 +0100 Subject: Implement experimental Tiny mappings loader ~ This will need some tests and more security checks --- src/main/java/cuchaz/enigma/gui/Gui.java | 4 + src/main/java/cuchaz/enigma/gui/GuiController.java | 8 ++ .../java/cuchaz/enigma/gui/elements/MenuBar.java | 16 ++++ .../java/cuchaz/enigma/mapping/ClassMapping.java | 13 +++ src/main/java/cuchaz/enigma/mapping/Mappings.java | 2 +- .../cuchaz/enigma/mapping/MappingsTinyReader.java | 99 ++++++++++++++++++++++ 6 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java (limited to 'src') diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java index 9f8d6fc..77065a9 100644 --- a/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/src/main/java/cuchaz/enigma/gui/Gui.java @@ -63,6 +63,7 @@ public class Gui { // state public EntryReference reference; public JFileChooser jarFileChooser; + public JFileChooser tinyMappingsFileChooser; public JFileChooser enigmaMappingsFileChooser; public JFileChooser exportSourceFileChooser; public JFileChooser exportJarFileChooser; @@ -105,6 +106,7 @@ public class Gui { // init file choosers this.jarFileChooser = new FileChooserFile(); + this.tinyMappingsFileChooser = new FileChooserFile(); this.enigmaMappingsFileChooser = new FileChooserAny(); this.exportSourceFileChooser = new FileChooserFolder(); this.exportJarFileChooser = new FileChooserFile(); @@ -314,6 +316,7 @@ public class Gui { // update menu this.menuBar.closeJarMenu.setEnabled(true); + this.menuBar.openTinyMappingsMenu.setEnabled(true); this.menuBar.openEnigmaMappingsMenu.setEnabled(true); this.menuBar.saveMappingsMenu.setEnabled(false); this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true); @@ -336,6 +339,7 @@ public class Gui { // update menu this.menuBar.closeJarMenu.setEnabled(false); + this.menuBar.openTinyMappingsMenu.setEnabled(false); this.menuBar.openEnigmaMappingsMenu.setEnabled(false); this.menuBar.saveMappingsMenu.setEnabled(false); this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false); diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java index 1b461da..c3cdbf8 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -71,6 +71,14 @@ public class GuiController { refreshCurrentClass(); } + public void openTinyMappings(File file) throws IOException, MappingParseException { + this.deobfuscator.setMappings(new MappingsTinyReader().read(file)); + this.isDirty = false; + this.gui.setMappingsFile(file); + refreshClasses(); + refreshCurrentClass(); + } + public void saveMappings(File file) throws IOException { Mappings mappings = this.deobfuscator.getMappings(); switch (mappings.getOriginMappingFormat()) { diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index cd11aca..e446c5a 100644 --- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java @@ -14,6 +14,7 @@ public class MenuBar extends JMenuBar { public final JMenuItem closeJarMenu; public final JMenuItem openEnigmaMappingsMenu; + public final JMenuItem openTinyMappingsMenu; public final JMenuItem saveMappingsMenu; public final JMenuItem saveMappingEnigmaFileMenu; public final JMenuItem saveMappingEnigmaDirectoryMenu; @@ -71,6 +72,21 @@ public class MenuBar extends JMenuBar { } }); this.openEnigmaMappingsMenu = item; + + item = new JMenuItem("Tiny"); + openMenu.add(item); + item.addActionListener(event -> { + if (this.gui.tinyMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { + try { + this.gui.getController().openTinyMappings(this.gui.tinyMappingsFileChooser.getSelectedFile()); + } catch (IOException ex) { + throw new Error(ex); + } catch (MappingParseException ex) { + JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage()); + } + } + }); + this.openTinyMappingsMenu = item; } { JMenuItem item = new JMenuItem("Save Mappings"); diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java index 178dd3c..1d1f558 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java @@ -23,6 +23,7 @@ public class ClassMapping implements Comparable { private String obfFullName; private String obfSimpleName; private String deobfName; + private String deobfFullName; private String previousDeobfName; private Map innerClassesByObfSimple; private Map innerClassesByObfFull; @@ -33,6 +34,7 @@ public class ClassMapping implements Comparable { private Map methodsByDeobf; private boolean isDirty; private Mappings.EntryModifier modifier; + private boolean deobfInner; public ClassMapping(String obfFullName) { this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); @@ -497,6 +499,10 @@ public class ClassMapping implements Comparable { return new ClassEntry(obfFullName); } + public ClassEntry getDeObfEntry() { + return deobfFullName != null ? new ClassEntry(deobfFullName) : null; + } + public boolean isDirty() { return isDirty; } @@ -534,4 +540,11 @@ public class ClassMapping implements Comparable { this.isDirty = true; } } + + // Used for tiny parsing to keep track of deobfuscate inner classes + public ClassMapping setDeobInner(String deobName) + { + this.deobfFullName = deobName; + return this; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java index 8aa463f..cf78ca3 100644 --- a/src/main/java/cuchaz/enigma/mapping/Mappings.java +++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java @@ -230,7 +230,7 @@ public class Mappings { } public enum FormatType { - ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE + ENIGMA_FILE, ENIGMA_DIRECTORY, TINY_FILE, SRG_FILE } public enum EntryModifier { diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java new file mode 100644 index 0000000..b2ba191 --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java @@ -0,0 +1,99 @@ +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 Type(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED); + } + + public MethodMapping readMethod(String[] parts) { + return new MethodMapping(parts[3], new Signature(parts[2]), parts[4]); + } + + public Mappings read(File file) throws IOException, MappingParseException { + Mappings mappings = new Mappings(Mappings.FormatType.TINY_FILE); + System.out.println(file); + List lines = Files.readAllLines(file.toPath(), Charsets.UTF_8); + Map classMappingMap = Maps.newHashMap(); + String header = lines.remove(0); + for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { + String line = lines.get(lineNumber); + String[] parts = line.split("\\s"); + try { + String token = parts[0]; + ClassMapping classMapping; + switch (token) { + case "CLASS": + if (classMappingMap.containsKey(parts[1])) { + classMapping = classMappingMap.get(parts[1]); + String deobName = parts[2].contains("$") ? + parts[2].substring(parts[2].lastIndexOf('$') + 1) : + parts[2]; + classMappingMap.put(parts[2], classMapping.setDeobInner(parts[2])); + classMapping.setDeobfName(deobName); + classMapping.resetDirty(); + } else + classMapping = readClass(parts); + classMappingMap.put(parts[1], classMapping); + break; + case "FIELD": + classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1])); + //throw new MappingParseException(file, lineNumber, "Cannot find class '" + parts[1] + "' declaration!"); + classMapping.addFieldMapping(readField(parts)); + break; + case "METHOD": + classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1])); + //throw new MappingParseException(file, lineNumber, "Cannot find class '" + parts[1] + "' declaration!"); + classMapping.addMethodMapping(readMethod(parts)); + break; + default: + throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !"); + } + } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { + throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line); + } + } + + List toRegister = new ArrayList<>(classMappingMap.values()); + for (ClassMapping classMapping : toRegister) { + ClassEntry obEntry = classMapping.getObfEntry(); + ClassEntry deobEntry = classMapping.getDeObfEntry(); + try { + if (obEntry.isInnerClass()) { + ClassMapping parent = classMappingMap.get(obEntry.getOuterClassName()); + if (parent == null) { + parent = new ClassMapping(obEntry.getOuterClassName()); + classMappingMap.put(obEntry.getOuterClassName(), parent); + mappings.addClassMapping(parent); + } + parent.addInnerClassMapping(classMapping); + } else if (deobEntry != null && deobEntry.isInnerClass()) + classMappingMap.get(deobEntry.getOuterClassName()).addInnerClassMapping(classMapping); + else + mappings.addClassMapping(classMapping); + } catch (MappingConflict e) { + throw new MappingParseException(file, -1, e.getMessage()); + } + } + lines.clear(); + classMappingMap.clear(); + return mappings; + } +} -- cgit v1.2.3