From b280104d2f926ab74772cef2bf1602663cefa312 Mon Sep 17 00:00:00 2001 From: Thog Date: Tue, 16 May 2017 00:24:29 +0200 Subject: Remove the converter + some reorganization --- .../cuchaz/enigma/bytecode/ClassTranslator.java | 165 --------------------- .../cuchaz/enigma/bytecode/InnerClassWriter.java | 151 ------------------- .../enigma/bytecode/LocalVariableRenamer.java | 150 ------------------- .../enigma/bytecode/MethodParameterWriter.java | 67 --------- .../bytecode/translators/ClassTranslator.java | 161 ++++++++++++++++++++ .../bytecode/translators/InnerClassWriter.java | 144 ++++++++++++++++++ .../translators/LocalVariableTranslator.java | 142 ++++++++++++++++++ .../translators/MethodParameterTranslator.java | 62 ++++++++ 8 files changed, 509 insertions(+), 533 deletions(-) delete mode 100644 src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java (limited to 'src/main/java/cuchaz/enigma/bytecode') diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java deleted file mode 100644 index 1ebf656..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java +++ /dev/null @@ -1,165 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *
- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtField; -import javassist.CtMethod; -import javassist.bytecode.*; - -public class ClassTranslator { - - private Translator translator; - - public ClassTranslator(Translator translator) { - this.translator = translator; - } - - public void translate(CtClass c) { - - // NOTE: the order of these translations is very important - - // translate all the field and method references in the code by editing the constant pool - ConstPool constants = c.getClassFile().getConstPool(); - ConstPoolEditor editor = new ConstPoolEditor(constants); - for (int i = 1; i < constants.getSize(); i++) { - switch (constants.getTag(i)) { - - case ConstPool.CONST_Fieldref: { - - // translate the name and type - FieldEntry entry = EntryFactory.getFieldEntry( - Descriptor.toJvmName(constants.getFieldrefClassName(i)), - constants.getFieldrefName(i), - constants.getFieldrefType(i) - ); - FieldEntry translatedEntry = this.translator.translateEntry(entry); - if (!entry.equals(translatedEntry)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); - } - } - break; - - case ConstPool.CONST_Methodref: - case ConstPool.CONST_InterfaceMethodref: { - - // translate the name and type (ie signature) - BehaviorEntry entry = EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(editor.getMemberrefClassname(i)), - editor.getMemberrefName(i), - editor.getMemberrefType(i) - ); - BehaviorEntry translatedEntry = this.translator.translateEntry(entry); - if (!entry.equals(translatedEntry)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); - } - } - break; - default: - break; - } - } - - ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); - Mappings.EntryModifier modifier = this.translator.getModifier(classEntry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(c, modifier); - - // translate all the fields - for (CtField field : c.getDeclaredFields()) { - - // translate the name - FieldEntry entry = EntryFactory.getFieldEntry(field); - String translatedName = this.translator.translate(entry); - modifier = this.translator.getModifier(entry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(field, modifier); - - if (translatedName != null) { - field.setName(translatedName); - } - - // translate the type - Type translatedType = this.translator.translateType(entry.getType()); - field.getFieldInfo().setDescriptor(translatedType.toString()); - } - - // translate all the methods and constructors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); - - modifier = this.translator.getModifier(entry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(behavior, modifier); - - if (behavior instanceof CtMethod) { - CtMethod method = (CtMethod) behavior; - - // translate the name - String translatedName = this.translator.translate(entry); - if (translatedName != null) { - method.setName(translatedName); - } - } - - if (entry.getSignature() != null) { - // translate the signature - Signature translatedSignature = this.translator.translateSignature(entry.getSignature()); - behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); - } - } - - // translate the EnclosingMethod attribute - EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); - if (enclosingMethodAttr != null) { - - if (enclosingMethodAttr.methodIndex() == 0) { - BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); - BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); - c.getClassFile().addAttribute(new EnclosingMethodAttribute( - constants, - deobfBehaviorEntry.getClassName() - )); - } else { - BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(enclosingMethodAttr.className()), - enclosingMethodAttr.methodName(), - enclosingMethodAttr.methodDescriptor() - ); - BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); - c.getClassFile().addAttribute(new EnclosingMethodAttribute( - constants, - deobfBehaviorEntry.getClassName(), - deobfBehaviorEntry.getName(), - deobfBehaviorEntry.getSignature().toString() - )); - } - } - - // translate all the class names referenced in the code - // the above code only changed method/field/reference names and types, but not the rest of the class references - ClassRenamer.renameClasses(c, this.translator); - - // translate the source file attribute too - ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry); - if (deobfClassEntry != null) { - String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; - c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); - } - InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (attr != null) - InnerClassWriter.changeModifier(c, attr, translator); - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java deleted file mode 100644 index f1c3dd7..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java +++ /dev/null @@ -1,151 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *
- * Contributors:
- * Jeff Martin - initial API and implementation
- ******************************************************************************/
-
-package cuchaz.enigma.bytecode;
-
-import com.google.common.collect.Lists;
-import cuchaz.enigma.analysis.JarIndex;
-import cuchaz.enigma.mapping.*;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.NotFoundException;
-import javassist.bytecode.*;
-
-import java.util.Collection;
-import java.util.List;
-
-public class InnerClassWriter {
-
- private JarIndex index;
- private Translator deobfuscatorTranslator;
-
- public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) {
- this.index = index;
- this.deobfuscatorTranslator = deobfuscatorTranslator;
- }
-
- // FIXME: modifier is not applied to inner class
- public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) {
- ClassPool pool = c.getClassPool();
- for (int i = 0; i < attr.tableLength(); i++) {
-
- String innerName = attr.innerClass(i);
- // get the inner class full name (which has already been translated)
- ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
- try {
- CtClass innerClass = pool.get(innerName);
- Mappings.EntryModifier modifier = translator.getModifier(classEntry);
- if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
- ClassRenamer.applyModifier(innerClass, modifier);
- } catch (NotFoundException e) {
- // This shouldn't be possible in theory
- //e.printStackTrace();
- }
- }
- }
-
- public void write(CtClass c) {
-
- // don't change anything if there's already an attribute there
- InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
- if (oldAttr != null) {
- // bail!
- return;
- }
-
- ClassEntry obfClassEntry = EntryFactory.getClassEntry(c);
- List
- * Contributors:
- * Jeff Martin - initial API and implementation
- ******************************************************************************/
-
-package cuchaz.enigma.bytecode;
-
-import cuchaz.enigma.mapping.*;
-import javassist.CtBehavior;
-import javassist.CtClass;
-import javassist.bytecode.*;
-
-public class LocalVariableRenamer {
-
- private Translator translator;
-
- public LocalVariableRenamer(Translator translator) {
- this.translator = translator;
- }
-
- public void rename(CtClass c) {
- for (CtBehavior behavior : c.getDeclaredBehaviors()) {
-
- // if there's a local variable table, just rename everything to v1, v2, v3, ... for now
- CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
- if (codeAttribute == null) {
- continue;
- }
-
- BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
- ConstPool constants = c.getClassFile().getConstPool();
-
- LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
- if (table != null) {
- renameLVT(behaviorEntry, constants, table, c);
- }
-
- LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag);
- if (typeTable != null) {
- renameLVTT(typeTable, table);
- }
- }
- }
-
- // DEBUG
- @SuppressWarnings("unused")
- private void dumpTable(LocalVariableAttribute table) {
- for (int i = 0; i < table.tableLength(); i++) {
- System.out.println(String.format("\t%d (%d): %s %s",
- i, table.index(i), table.variableName(i), table.descriptor(i)
- ));
- }
- }
-
- private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) {
-
- // skip empty tables
- if (table.tableLength() <= 0) {
- return;
- }
-
- // where do we start counting variables?
- int starti = 0;
- if (table.variableName(0).equals("this")) {
- // skip the "this" variable
- starti++;
- }
-
- // rename method arguments first
- int numArgs = 0;
- if (behaviorEntry.getSignature() != null) {
- numArgs = behaviorEntry.getSignature().getArgumentTypes().size();
- boolean isNestedClassConstructor = false;
-
- // If the behavior is a constructor and if it have more than one arg, it's probably from a nested!
- if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) {
- // Get the first arg type
- Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0);
-
- // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class
- if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) {
- isNestedClassConstructor = true;
- numArgs--;
- }
- }
-
- for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) {
- int argi = i - starti;
- if (ctClass.isEnum())
- argi += 2;
- if (behaviorEntry.getClassEntry().getName().contains("ahd") && behaviorEntry instanceof ConstructorEntry)
- System.out.println(behaviorEntry.getClassEntry() + " " + i);
- String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, ""));
- if (argName == null) {
- int argIndex = isNestedClassConstructor ? argi + 1 : argi;
- if (ctClass.isEnum())
- argIndex -= 2;
- Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex);
- // Unfortunately each of these have different name getters, so they have different code paths
- if (argType.isPrimitive()) {
- Type.Primitive argCls = argType.getPrimitive();
- argName = "a" + argCls.name() + (argIndex + 1);
- } else if (argType.isArray()) {
- // List types would require this whole block again, so just go with aListx
- argName = "aList" + (argIndex + 1);
- } else if (argType.isClass()) {
- ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry());
- argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1);
- } else {
- argName = "a" + (argIndex + 1);
- }
- }
- renameVariable(table, i, constants.addUtf8Info(argName));
- }
- }
-
- // then rename the rest of the args, if any
- for (int i = starti + numArgs; i < table.tableLength(); i++) {
- int firstIndex = Math.min(table.index(starti + numArgs), table.index(i));
- renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1)));
- }
- }
-
- private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) {
- // rename args to the same names as in the LVT
- for (int i = 0; i < typeTable.tableLength(); i++) {
- renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i)));
- }
- }
-
- private void renameVariable(LocalVariableAttribute table, int i, int stringId) {
- // based off of LocalVariableAttribute.nameIndex()
- ByteArray.write16bit(stringId, table.get(), i * 10 + 6);
- }
-
- private int getNameIndex(LocalVariableAttribute table, int index) {
- for (int i = 0; i < table.tableLength(); i++) {
- if (table.index(i) == index) {
- return table.nameIndex(i);
- }
- }
- return 0;
- }
-}
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java
deleted file mode 100644
index d63572e..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015 Jeff Martin.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Lesser General Public
- * License v3.0 which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/lgpl.html
- *
- * Contributors:
- * Jeff Martin - initial API and implementation
- ******************************************************************************/
-
-package cuchaz.enigma.bytecode;
-
-import cuchaz.enigma.mapping.*;
-import javassist.CtBehavior;
-import javassist.CtClass;
-import javassist.bytecode.CodeAttribute;
-import javassist.bytecode.LocalVariableAttribute;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class MethodParameterWriter {
-
- private Translator translator;
-
- public MethodParameterWriter(Translator translator) {
- this.translator = translator;
- }
-
- public void writeMethodArguments(CtClass c) {
-
- // Procyon will read method arguments from the "MethodParameters" attribute, so write those
- for (CtBehavior behavior : c.getDeclaredBehaviors()) {
-
- // if there's a local variable table here, don't write a MethodParameters attribute
- // let the local variable writer deal with it instead
- // procyon starts doing really weird things if we give it both attributes
- CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
- if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) {
- continue;
- }
-
- BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
-
- // get the number of arguments
- Signature signature = behaviorEntry.getSignature();
- if (signature == null) {
- // static initializers have no signatures, or arguments
- continue;
- }
- int numParams = signature.getArgumentTypes().size();
- if (numParams <= 0) {
- continue;
- }
-
- // get the list of argument names
- List
+ * Contributors:
+ * Jeff Martin - initial API and implementation
+ ******************************************************************************/
+
+package cuchaz.enigma.bytecode.translators;
+
+import cuchaz.enigma.bytecode.ClassRenamer;
+import cuchaz.enigma.bytecode.ConstPoolEditor;
+import cuchaz.enigma.mapping.*;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.bytecode.*;
+
+public class ClassTranslator {
+
+ public static void translate(Translator translator, CtClass c) {
+
+ // NOTE: the order of these translations is very important
+
+ // translate all the field and method references in the code by editing the constant pool
+ ConstPool constants = c.getClassFile().getConstPool();
+ ConstPoolEditor editor = new ConstPoolEditor(constants);
+ for (int i = 1; i < constants.getSize(); i++) {
+ switch (constants.getTag(i)) {
+
+ case ConstPool.CONST_Fieldref: {
+
+ // translate the name and type
+ FieldEntry entry = EntryFactory.getFieldEntry(
+ Descriptor.toJvmName(constants.getFieldrefClassName(i)),
+ constants.getFieldrefName(i),
+ constants.getFieldrefType(i)
+ );
+ FieldEntry translatedEntry = translator.translateEntry(entry);
+ if (!entry.equals(translatedEntry)) {
+ editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString());
+ }
+ }
+ break;
+
+ case ConstPool.CONST_Methodref:
+ case ConstPool.CONST_InterfaceMethodref: {
+
+ // translate the name and type (ie signature)
+ BehaviorEntry entry = EntryFactory.getBehaviorEntry(
+ Descriptor.toJvmName(editor.getMemberrefClassname(i)),
+ editor.getMemberrefName(i),
+ editor.getMemberrefType(i)
+ );
+ BehaviorEntry translatedEntry = translator.translateEntry(entry);
+ if (!entry.equals(translatedEntry)) {
+ editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
+ Mappings.EntryModifier modifier = translator.getModifier(classEntry);
+ if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
+ ClassRenamer.applyModifier(c, modifier);
+
+ // translate all the fields
+ for (CtField field : c.getDeclaredFields()) {
+
+ // translate the name
+ FieldEntry entry = EntryFactory.getFieldEntry(field);
+ String translatedName = translator.translate(entry);
+ modifier = translator.getModifier(entry);
+ if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
+ ClassRenamer.applyModifier(field, modifier);
+
+ if (translatedName != null) {
+ field.setName(translatedName);
+ }
+
+ // translate the type
+ Type translatedType = translator.translateType(entry.getType());
+ field.getFieldInfo().setDescriptor(translatedType.toString());
+ }
+
+ // translate all the methods and constructors
+ for (CtBehavior behavior : c.getDeclaredBehaviors()) {
+
+ BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior);
+
+ modifier = translator.getModifier(entry);
+ if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
+ ClassRenamer.applyModifier(behavior, modifier);
+
+ if (behavior instanceof CtMethod) {
+ CtMethod method = (CtMethod) behavior;
+
+ // translate the name
+ String translatedName = translator.translate(entry);
+ if (translatedName != null) {
+ method.setName(translatedName);
+ }
+ }
+
+ if (entry.getSignature() != null) {
+ // translate the signature
+ Signature translatedSignature = translator.translateSignature(entry.getSignature());
+ behavior.getMethodInfo().setDescriptor(translatedSignature.toString());
+ }
+ }
+
+ // translate the EnclosingMethod attribute
+ EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag);
+ if (enclosingMethodAttr != null) {
+
+ if (enclosingMethodAttr.methodIndex() == 0) {
+ BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className()));
+ BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry);
+ c.getClassFile().addAttribute(new EnclosingMethodAttribute(
+ constants,
+ deobfBehaviorEntry.getClassName()
+ ));
+ } else {
+ BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(
+ Descriptor.toJvmName(enclosingMethodAttr.className()),
+ enclosingMethodAttr.methodName(),
+ enclosingMethodAttr.methodDescriptor()
+ );
+ BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry);
+ c.getClassFile().addAttribute(new EnclosingMethodAttribute(
+ constants,
+ deobfBehaviorEntry.getClassName(),
+ deobfBehaviorEntry.getName(),
+ deobfBehaviorEntry.getSignature().toString()
+ ));
+ }
+ }
+
+ // translate all the class names referenced in the code
+ // the above code only changed method/field/reference names and types, but not the rest of the class references
+ ClassRenamer.renameClasses(c, translator);
+
+ // translate the source file attribute too
+ ClassEntry deobfClassEntry = translator.translateEntry(classEntry);
+ if (deobfClassEntry != null) {
+ String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java";
+ c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile));
+ }
+ InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
+ if (attr != null)
+ InnerClassWriter.changeModifier(c, attr, translator);
+ }
+}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java
new file mode 100644
index 0000000..0e35938
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Jeff Martin.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public
+ * License v3.0 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Contributors:
+ * Jeff Martin - initial API and implementation
+ ******************************************************************************/
+
+package cuchaz.enigma.bytecode.translators;
+
+import com.google.common.collect.Lists;
+import cuchaz.enigma.analysis.JarIndex;
+import cuchaz.enigma.bytecode.ClassRenamer;
+import cuchaz.enigma.mapping.*;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.*;
+
+import java.util.Collection;
+import java.util.List;
+
+public class InnerClassWriter {
+
+ // FIXME: modifier is not applied to inner class
+ public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) {
+ ClassPool pool = c.getClassPool();
+ for (int i = 0; i < attr.tableLength(); i++) {
+
+ String innerName = attr.innerClass(i);
+ // get the inner class full name (which has already been translated)
+ ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
+ try {
+ CtClass innerClass = pool.get(innerName);
+ Mappings.EntryModifier modifier = translator.getModifier(classEntry);
+ if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
+ ClassRenamer.applyModifier(innerClass, modifier);
+ } catch (NotFoundException e) {
+ // This shouldn't be possible in theory
+ //e.printStackTrace();
+ }
+ }
+ }
+
+ public static void write(JarIndex index, CtClass c) {
+
+ // don't change anything if there's already an attribute there
+ InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
+ if (oldAttr != null) {
+ // bail!
+ return;
+ }
+
+ ClassEntry obfClassEntry = EntryFactory.getClassEntry(c);
+ List
+ * Contributors:
+ * Jeff Martin - initial API and implementation
+ ******************************************************************************/
+
+package cuchaz.enigma.bytecode.translators;
+
+import cuchaz.enigma.mapping.*;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.bytecode.*;
+
+public class LocalVariableTranslator {
+
+ public static void translate(Translator translator, CtClass c) {
+ for (CtBehavior behavior : c.getDeclaredBehaviors()) {
+
+ // if there's a local variable table, just rename everything to v1, v2, v3, ... for now
+ CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
+ if (codeAttribute == null) {
+ continue;
+ }
+
+ BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
+ ConstPool constants = c.getClassFile().getConstPool();
+
+ LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
+ if (table != null) {
+ renameLVT(translator, behaviorEntry, constants, table, c);
+ }
+
+ LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag);
+ if (typeTable != null) {
+ renameLVTT(typeTable, table);
+ }
+ }
+ }
+
+ // DEBUG
+ @SuppressWarnings("unused")
+ private static void dumpTable(LocalVariableAttribute table) {
+ for (int i = 0; i < table.tableLength(); i++) {
+ System.out.println(String.format("\t%d (%d): %s %s",
+ i, table.index(i), table.variableName(i), table.descriptor(i)
+ ));
+ }
+ }
+
+ private static void renameLVT(Translator translator, BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) {
+
+ // skip empty tables
+ if (table.tableLength() <= 0) {
+ return;
+ }
+
+ // where do we start counting variables?
+ int starti = 0;
+ if (table.variableName(0).equals("this")) {
+ // skip the "this" variable
+ starti++;
+ }
+
+ // rename method arguments first
+ int numArgs = 0;
+ if (behaviorEntry.getSignature() != null) {
+ numArgs = behaviorEntry.getSignature().getArgumentTypes().size();
+ boolean isNestedClassConstructor = false;
+
+ // If the behavior is a constructor and if it have more than one arg, it's probably from a nested!
+ if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) {
+ // Get the first arg type
+ Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0);
+
+ // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class
+ if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) {
+ isNestedClassConstructor = true;
+ numArgs--;
+ }
+ }
+
+ for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) {
+ int argi = i - starti;
+ if (ctClass.isEnum())
+ argi += 2;
+ String argName = translator.translate(new ArgumentEntry(behaviorEntry, argi, ""));
+ if (argName == null) {
+ int argIndex = isNestedClassConstructor ? argi + 1 : argi;
+ if (ctClass.isEnum())
+ argIndex -= 2;
+ Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex);
+ // Unfortunately each of these have different name getters, so they have different code paths
+ if (argType.isPrimitive()) {
+ Type.Primitive argCls = argType.getPrimitive();
+ argName = "a" + argCls.name() + (argIndex + 1);
+ } else if (argType.isArray()) {
+ // List types would require this whole block again, so just go with aListx
+ argName = "aList" + (argIndex + 1);
+ } else if (argType.isClass()) {
+ ClassEntry argClsTrans = translator.translateEntry(argType.getClassEntry());
+ argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1);
+ } else {
+ argName = "a" + (argIndex + 1);
+ }
+ }
+ renameVariable(table, i, constants.addUtf8Info(argName));
+ }
+ }
+
+ // then rename the rest of the args, if any
+ for (int i = starti + numArgs; i < table.tableLength(); i++) {
+ int firstIndex = Math.min(table.index(starti + numArgs), table.index(i));
+ renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1)));
+ }
+ }
+
+ private static void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) {
+ // rename args to the same names as in the LVT
+ for (int i = 0; i < typeTable.tableLength(); i++) {
+ renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i)));
+ }
+ }
+
+ private static void renameVariable(LocalVariableAttribute table, int i, int stringId) {
+ // based off of LocalVariableAttribute.nameIndex()
+ ByteArray.write16bit(stringId, table.get(), i * 10 + 6);
+ }
+
+ private static int getNameIndex(LocalVariableAttribute table, int index) {
+ for (int i = 0; i < table.tableLength(); i++) {
+ if (table.index(i) == index) {
+ return table.nameIndex(i);
+ }
+ }
+ return 0;
+ }
+}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java
new file mode 100644
index 0000000..4e632b9
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Jeff Martin.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public
+ * License v3.0 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Contributors:
+ * Jeff Martin - initial API and implementation
+ ******************************************************************************/
+
+package cuchaz.enigma.bytecode.translators;
+
+import cuchaz.enigma.bytecode.MethodParametersAttribute;
+import cuchaz.enigma.mapping.*;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.LocalVariableAttribute;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MethodParameterTranslator {
+
+ public static void translate(Translator translator, CtClass c) {
+
+ // Procyon will read method arguments from the "MethodParameters" attribute, so write those
+ for (CtBehavior behavior : c.getDeclaredBehaviors()) {
+
+ // if there's a local variable table here, don't write a MethodParameters attribute
+ // let the local variable writer deal with it instead
+ // procyon starts doing really weird things if we give it both attributes
+ CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
+ if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) {
+ continue;
+ }
+
+ BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
+
+ // get the number of arguments
+ Signature signature = behaviorEntry.getSignature();
+ if (signature == null) {
+ // static initializers have no signatures, or arguments
+ continue;
+ }
+ int numParams = signature.getArgumentTypes().size();
+ if (numParams <= 0) {
+ continue;
+ }
+
+ // get the list of argument names
+ List