nl.hardijzer.fw.applysrg.Method.java Source code

Java tutorial

Introduction

Here is the source code for nl.hardijzer.fw.applysrg.Method.java

Source

/*
 * Copyright 2012 Frans-Willem Hardijzer
 * This file is part of SrgTools.
 *
 * SrgTools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SrgTools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with SrgTools.  If not, see <http://www.gnu.org/licenses/>.
 */

package nl.hardijzer.fw.applysrg;

import java.util.zip.*;
import java.io.*;
import java.util.*;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.*;

class Method implements Comparable<Method> {
    public String name;
    public String desc;

    public Method(String strName, String strArguments) {
        this.name = strName;
        this.desc = strArguments;
    }

    @Override
    public int compareTo(Method b) {
        int cmpName = name.compareTo(b.name);
        int cmpDesc = desc.compareTo(b.desc);
        if (cmpName != 0)
            return cmpName;
        return cmpDesc;
    }
}

class MappedClass {
    public String strNewName;
    public Map<String, String> mapFields;
    public Map<Method, String> mapMethods;

    public MappedClass(String strNewName) {
        this.strNewName = strNewName;
        this.mapFields = new TreeMap<String, String>();
        this.mapMethods = new TreeMap<Method, String>();
    }
}

class ClassInfo {
    public Set<String> setFields;
    public Set<Method> setMethods;
    public Set<String> setInheritance;

    public ClassInfo() {
        setFields = new TreeSet<String>();
        setMethods = new TreeSet<Method>();
        setInheritance = new TreeSet<String>();
    }
}

class MyRemapper extends Remapper {
    public Map<String, MappedClass> mapClasses;
    public Map<String, ClassInfo> mapClassInfo;
    public Map<String, String> mapPackages;

    public MyRemapper(Map<String, MappedClass> mapClasses, Map<String, ClassInfo> mapInheritance,
            Map<String, String> mapPackages) {
        super();
        this.mapClasses = mapClasses;
        this.mapClassInfo = mapInheritance;
        this.mapPackages = mapPackages;
    }

    public String mapPackage(String strPackage) {
        String strMapped = mapPackages.get(strPackage);
        if (strMapped != null)
            return strMapped;
        if (strPackage.equals("."))
            return strPackage;
        int nSplit = strPackage.lastIndexOf('/');
        String strUp = ".";
        if (nSplit != -1) {
            strUp = strPackage.substring(0, nSplit);
            strPackage = strPackage.substring(nSplit + 1);
        }
        String strUpMapped = mapPackage(strUp);
        if (strUpMapped.equals("."))
            return strPackage;
        return strUpMapped + "/" + strPackage;
    }

    @Override
    public String map(String typeName) {
        MappedClass other = mapClasses.get(typeName);
        if (other != null)
            return other.strNewName;
        int nSplit = typeName.lastIndexOf('$');
        if (nSplit != -1) {
            return map(typeName.substring(0, nSplit)) + typeName.substring(nSplit);
        }
        String strPackage = ".";
        nSplit = typeName.lastIndexOf('/');
        if (nSplit != -1) {
            strPackage = typeName.substring(0, nSplit);
            typeName = typeName.substring(nSplit + 1);
        }
        String strPackageMapped = mapPackage(strPackage);
        if (strPackageMapped.equals("."))
            return typeName;
        return strPackageMapped + "/" + typeName;
    }

    public String mapMethodNameDirect(String owner, Method m) {
        MappedClass mappedClass = mapClasses.get(owner);
        if (mappedClass != null) {
            String strMapped = mappedClass.mapMethods.get(m);
            if (strMapped != null) {
                //System.out.println("Mapping method "+owner+"/"+m.name+" to new name "+strMapped);
                return strMapped;
            }
        }
        return null;
    }

    public String mapFieldNameDirect(String owner, String f) {
        MappedClass mappedClass = mapClasses.get(owner);
        if (mappedClass != null) {
            String strMapped = mappedClass.mapFields.get(f);
            if (strMapped != null) {
                //System.out.println("Mapping field "+owner+"/"+f+" to new name "+strMapped);
                return strMapped;
            }
        }
        return null;
    }

    //Method can point to a method up the chain, but also further up the chain, so returns the first owner with a mapping associated.
    public String locateMethod(String owner, Method m) {
        Queue<String> q = new LinkedList<String>();
        q.add(owner);
        while (!q.isEmpty()) {
            String strOwner = q.remove();
            ClassInfo info = mapClassInfo.get(strOwner);
            if (info == null)
                continue;
            for (String inherit : info.setInheritance)
                q.add(inherit);
            if (mapClasses.containsKey(strOwner) && info.setMethods.contains(m))
                return strOwner;
        }
        return null;
    }

    @Override
    public String mapMethodName(String owner, String name, String desc) {
        Method m = new Method(name, desc);
        String strActualOwner = locateMethod(owner, m);
        //if (strActualOwner!=null)
        //   System.out.println("Tracked method "+owner+"/"+name+" "+desc+" to "+strActualOwner);
        String strMapped = mapMethodNameDirect((strActualOwner != null) ? strActualOwner : owner, m);
        return (strMapped == null) ? name : strMapped;
    }

    //Field always maps to the first field found up the chain.
    public String locateField(String owner, String f) {
        Queue<String> q = new LinkedList<String>();
        q.add(owner);
        while (!q.isEmpty()) {
            String strOwner = q.remove();
            ClassInfo info = mapClassInfo.get(strOwner);
            if (info == null)
                continue;
            for (String inherit : info.setInheritance)
                q.add(inherit);
            if (info.setFields.contains(f))
                return strOwner;
        }
        return null;
    }

    @Override
    public String mapFieldName(String owner, String name, String desc) {
        String strActualOwner = locateField(owner, name);
        //if (strActualOwner!=null)
        //   System.out.println("Tracked field "+owner+"/"+name+" "+desc+" to "+strActualOwner);
        String strMapped = mapFieldNameDirect((strActualOwner != null) ? strActualOwner : owner, name);
        return (strMapped == null) ? name : strMapped;
    }
}

class InheritanceMapClassVisitor extends ClassVisitor {
    public String strName;
    public ClassInfo info;

    public InheritanceMapClassVisitor(int api) {
        super(api);
        strName = "";
        info = new ClassInfo();
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        this.strName = name;
        this.info.setInheritance.add(superName);
        for (int i = 0; i < interfaces.length; i++)
            this.info.setInheritance.add(interfaces[i]);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
        return null;
    }

    @Override
    public void visitAttribute(Attribute arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void visitEnd() {
        // TODO Auto-generated method stub

    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        this.info.setFields.add(name);
        return null;
    }

    @Override
    public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
        // TODO Auto-generated method stub

    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        this.info.setMethods.add(new Method(name, desc));
        return null;
    }

    @Override
    public void visitOuterClass(String arg0, String arg1, String arg2) {
        // TODO Auto-generated method stub
    }

    @Override
    public void visitSource(String arg0, String arg1) {
        // TODO Auto-generated method stub
    }

}

class ParseEntry {
    String strInputFilename;
    String strOutputFilename;

    public ParseEntry(String a, String b) {
        strInputFilename = a;
        strOutputFilename = b;
    }
};

public class ApplySrg {

    /**
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {
        String strInputFilename = null;
        String strOutputFilename = null;
        List<String> listInputSrg = new LinkedList<String>();
        List<String> listInputInheritance = new LinkedList<String>();
        List<ParseEntry> listTranslate = new LinkedList<ParseEntry>();
        for (int i = 0; i < args.length; i++) {
            if (args[i].equals("--srg"))
                listInputSrg.add(args[++i]);
            else if (args[i].equals("--inheritance") || args[i].equals("--inh"))
                listInputInheritance.add(args[++i]);
            else if (args[i].equals("--in"))
                strInputFilename = args[++i];
            else if (args[i].equals("--out"))
                strOutputFilename = args[++i];
            else if (args[i].equals("--translate")) {
                String a = args[++i];
                String b = args[++i];
                listTranslate.add(new ParseEntry(a, b));
                listInputInheritance.add(a);
            }
        }
        if (strInputFilename != null && strOutputFilename != null) {
            listTranslate.add(new ParseEntry(strInputFilename, strOutputFilename));
            strInputFilename = strOutputFilename = null;
        }
        if (strInputFilename != null || strOutputFilename != null || listTranslate.size() < 1) {
            System.err.println("Usage: java -jar srgtool.jar apply [options]");
            System.err.println("Options:");
            System.err.println("--srg <srg file>\tLoads the SRG file");
            System.err.println("--inheritance <jar/zip>\tLoads inheritance map from jar");
            System.err.println("--in <jar/zip>");
            System.err.println("--out <jar/zip>");
            return;
        }

        if (strInputFilename != null)
            listInputInheritance.add(strInputFilename);

        Map<String, MappedClass> mapClasses = new TreeMap<String, MappedClass>();
        Map<String, String> mapPackages = new TreeMap<String, String>();
        for (String srg : listInputSrg) {
            System.out.println("Reading SRG: " + srg);
            BufferedReader brSrg = new BufferedReader(new FileReader(srg));
            String strLine;
            while ((strLine = brSrg.readLine()) != null) {
                String arrLine[] = strLine.split(" ");
                if (arrLine[0].equals("PK:")) {
                    mapPackages.put(arrLine[1], arrLine[2]);
                } else if (arrLine[0].equals("CL:")) {
                    String strFrom = arrLine[1];
                    String strTo = arrLine[2];
                    MappedClass mappedCurrent = mapClasses.get(strFrom);
                    if (mappedCurrent == null) {
                        mapClasses.put(strFrom, new MappedClass(strTo));
                    } else {
                        if (!mappedCurrent.strNewName.equals(strTo)) {
                            System.err.println("ERROR: Mismatching mappings found");
                            return;
                        }
                    }
                } else if (arrLine[0].equals("FD:")) {
                    String strFrom = arrLine[1];
                    String strTo = arrLine[2];
                    int nSplitFrom = strFrom.lastIndexOf('/');
                    int nSplitTo = strTo.lastIndexOf('/');
                    if (nSplitFrom == -1 || nSplitTo == -1) {
                        System.err.println("ERROR: Invalid field specification: '" + strLine);
                        return;
                    }
                    String strFromClass = strFrom.substring(0, nSplitFrom);
                    strFrom = strFrom.substring(nSplitFrom + 1);
                    String strToClass = strTo.substring(0, nSplitTo);
                    strTo = strTo.substring(nSplitTo + 1);
                    MappedClass mappedCurrent = mapClasses.get(strFromClass);
                    if (strFromClass.equals(strToClass) && mappedCurrent == null) {
                        mapClasses.put(strFromClass, mappedCurrent = new MappedClass(strToClass));
                    }
                    if (mappedCurrent == null || !mappedCurrent.strNewName.equals(strToClass)) {
                        System.err.println("ERROR: Class mapping invalid or non-existant on field");
                        System.err.println("Line: " + strLine);
                        System.err.println(strFromClass + " -> " + strToClass + " should have been "
                                + ((mappedCurrent == null) ? "null" : mappedCurrent.strNewName));
                        return;
                    }
                    mappedCurrent.mapFields.put(strFrom, strTo);
                } else if (arrLine[0].equals("MD:")) {
                    String strFrom = arrLine[1];
                    String strFromArguments = arrLine[2];
                    String strTo = arrLine[3];
                    String strToArguments = arrLine[4];
                    int nSplitFrom = strFrom.lastIndexOf('/');
                    int nSplitTo = strTo.lastIndexOf('/');
                    if (nSplitFrom == -1 || nSplitTo == -1) {
                        System.err.println("ERROR: Invalid method specification: '" + strLine);
                        return;
                    }
                    String strFromClass = strFrom.substring(0, nSplitFrom);
                    strFrom = strFrom.substring(nSplitFrom + 1);
                    String strToClass = strTo.substring(0, nSplitTo);
                    strTo = strTo.substring(nSplitTo + 1);
                    MappedClass mappedCurrent = mapClasses.get(strFromClass);
                    if (strFromClass.equals(strToClass) && mappedCurrent == null) {
                        mapClasses.put(strFromClass, mappedCurrent = new MappedClass(strToClass));
                    }
                    if (mappedCurrent == null || !mappedCurrent.strNewName.equals(strToClass)) {
                        System.err.println("ERROR: Class mapping invalid or non-existant on method");
                        System.err.println("Line: " + strLine);
                        System.err.println(strFromClass + " -> " + strToClass + " should have been "
                                + ((mappedCurrent == null) ? "null" : mappedCurrent.strNewName));
                        return;
                    }
                    //NOTE: arguments not saved, will be mapped automagically.
                    mappedCurrent.mapMethods.put(new Method(strFrom, strFromArguments), strTo);
                }
            }
        }
        System.out.println("Class map loaded of " + mapClasses.size() + " classes");
        Map<String, ClassInfo> mapClassInheritance = new HashMap<String, ClassInfo>();
        for (String inherit : listInputInheritance) {
            System.out.println("Parsing inheritance in " + inherit);
            //System.out.println("Parsing inheritance in "+inherit);
            ZipFile zipInherit = new ZipFile(inherit);
            Enumeration<? extends ZipEntry> entries = zipInherit.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (entry.isDirectory())
                    continue;
                if (entry.getName().endsWith(".class")) {
                    ClassReader cr = new ClassReader(zipInherit.getInputStream(entry));
                    InheritanceMapClassVisitor cvInheritance = new InheritanceMapClassVisitor(Opcodes.ASM5);
                    cr.accept(cvInheritance, 0);
                    mapClassInheritance.put(cvInheritance.strName, cvInheritance.info);
                }
            }
            zipInherit.close();
        }
        System.out.println("Inheritance map loaded of " + mapClassInheritance.size() + " classes");

        for (ParseEntry e : listTranslate) {
            System.out.println("Translating " + e.strInputFilename + " -> " + e.strOutputFilename);

            ZipFile zipInput = new ZipFile(e.strInputFilename);

            ZipOutputStream zipOutput = new ZipOutputStream(new FileOutputStream(e.strOutputFilename));
            Enumeration<? extends ZipEntry> entries = zipInput.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (entry.isDirectory())
                    continue;
                if (entry.getName().endsWith(".class")) {
                    ClassReader cr = new ClassReader(zipInput.getInputStream(entry));
                    ClassWriter cw = new ClassWriter(0);
                    Remapper remapper = new MyRemapper(mapClasses, mapClassInheritance, mapPackages);
                    RemappingClassAdapter ca = new RemappingClassAdapter(cw, remapper);
                    cr.accept(ca, ClassReader.EXPAND_FRAMES);
                    byte[] bOutput = cw.toByteArray();
                    String strName = entry.getName();
                    strName = strName.substring(0, strName.lastIndexOf('.'));
                    strName = remapper.mapType(strName);

                    ZipEntry entryCopy = new ZipEntry(strName + ".class");
                    entryCopy.setCompressedSize(-1);
                    entryCopy.setSize(bOutput.length);
                    zipOutput.putNextEntry(entryCopy);
                    zipOutput.write(bOutput);
                    zipOutput.closeEntry();
                } else {
                    ZipEntry entryCopy = new ZipEntry(entry);
                    entryCopy.setCompressedSize(-1);
                    zipOutput.putNextEntry(entryCopy);
                    InputStream is = zipInput.getInputStream(entry);
                    byte[] buffer = new byte[1024];
                    int read = 0;
                    while ((read = is.read(buffer)) != -1)
                        zipOutput.write(buffer, 0, read);
                    zipOutput.closeEntry();
                }
            }
            zipInput.close();
            zipOutput.close();
        }
        System.out.println("Done!");
    }

}