package immibis.bon.mcp; import immibis.bon.IProgressListener; import immibis.bon.Mapping; import immibis.bon.NameSet; import immibis.bon.mcp.MinecraftNameSet.Side; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; public class MappingLoader_MCP { public static class CantLoadMCPMappingException extends Exception { private static final long serialVersionUID = 1; public CantLoadMCPMappingException(String reason) { super(reason); } } // forward: obf -> searge -> mcp // reverse: mcp -> searge -> obf private Mapping forwardSRG, reverseSRG, forwardCSV, reverseCSV; private Map<String, Set<String>> srgMethodOwnersAndDescs = new HashMap<>(); // SRG name -> SRG owners private Map<String, Set<String>> srgFieldOwners = new HashMap<>(); // SRG name -> SRG owners private ExcFile excFileData; public MappingLoader_MCP() {} @Deprecated public MappingLoader_MCP(String mcVer, Side side, File mcpDir, IProgressListener progress) throws IOException, CantLoadMCPMappingException { File srgFile, excFile; int[] sideNumbers; switch(side) { case UNIVERSAL: sideNumbers = new int[] {2, 1, 0}; if(new File(mcpDir, "conf/packaged.srg").exists()) { srgFile = new File(mcpDir, "conf/packaged.srg"); excFile = new File(mcpDir, "conf/packaged.exc"); } else { srgFile = new File(mcpDir, "conf/joined.srg"); excFile = new File(mcpDir, "conf/joined.exc"); } break; case CLIENT: sideNumbers = new int[] {0}; srgFile = new File(mcpDir, "conf/client.srg"); if(new File(mcpDir, "conf/joined.exc").exists()) excFile = new File(mcpDir, "conf/joined.exc"); else excFile = new File(mcpDir, "conf/client.exc"); break; case SERVER: sideNumbers = new int[] {1}; srgFile = new File(mcpDir, "conf/server.srg"); if(new File(mcpDir, "conf/joined.exc").exists()) excFile = new File(mcpDir, "conf/joined.exc"); else excFile = new File(mcpDir, "conf/server.exc"); break; default: throw new AssertionError("side is "+side); } load(side, mcVer, new ExcFile(excFile), new SrgFile(srgFile, false), CsvFile.read(new File(mcpDir, "conf/fields.csv"), sideNumbers), CsvFile.read(new File(mcpDir, "conf/methods.csv"), sideNumbers), progress); } public void load(Side side, String mcVer, ExcFile excFile, SrgFile srgFile, Map<String, String> fieldNames, Map<String, String> methodNames, IProgressListener progress) throws CantLoadMCPMappingException { NameSet obfNS = new MinecraftNameSet(MinecraftNameSet.Type.OBF, side, mcVer); NameSet srgNS = new MinecraftNameSet(MinecraftNameSet.Type.SRG, side, mcVer); NameSet mcpNS = new MinecraftNameSet(MinecraftNameSet.Type.MCP, side, mcVer); forwardSRG = new Mapping(obfNS, srgNS); reverseSRG = new Mapping(srgNS, obfNS); forwardCSV = new Mapping(srgNS, mcpNS); reverseCSV = new Mapping(mcpNS, srgNS); if(progress != null) progress.setMax(3); if(progress != null) progress.set(0); excFileData = excFile; if(progress != null) progress.set(1); loadSRGMapping(srgFile); if(progress != null) progress.set(2); loadCSVMapping(fieldNames, methodNames); } private void loadSRGMapping(SrgFile srg) throws CantLoadMCPMappingException { forwardSRG.setDefaultPackage("net/minecraft/src/"); reverseSRG.addPrefix("net/minecraft/src/", ""); for(Map.Entry<String, String> entry : srg.classes.entrySet()) { String obfClass = entry.getKey(); String srgClass = entry.getValue(); forwardSRG.setClass(obfClass, srgClass); reverseSRG.setClass(srgClass, obfClass); } for(Map.Entry<String, String> entry : srg.fields.entrySet()) { String obfOwnerAndName = entry.getKey(); String srgName = entry.getValue(); String obfOwner = obfOwnerAndName.substring(0, obfOwnerAndName.lastIndexOf('/')); String obfName = obfOwnerAndName.substring(obfOwnerAndName.lastIndexOf('/') + 1); String srgOwner = srg.classes.get(obfOwner); // Enum values don't use the CSV and don't start with field_ if(srgName.startsWith("field_")) { if(srgFieldOwners.containsKey(srgName)) System.out.println("SRG field "+srgName+" appears in multiple classes (at least "+srgFieldOwners.get(srgName)+" and "+srgOwner+")"); Set<String> owners = srgFieldOwners.get(srgName); if(owners == null) srgFieldOwners.put(srgName, owners = new HashSet<String>()); owners.add(srgOwner); } forwardSRG.setField(obfOwner, obfName, srgName); reverseSRG.setField(srgOwner, srgName, obfName); } for(Map.Entry<String, String> entry : srg.methods.entrySet()) { String obfOwnerNameAndDesc = entry.getKey(); String srgName = entry.getValue(); String obfOwnerAndName = obfOwnerNameAndDesc.substring(0, obfOwnerNameAndDesc.indexOf('(')); String obfOwner = obfOwnerAndName.substring(0, obfOwnerAndName.lastIndexOf('/')); String obfName = obfOwnerAndName.substring(obfOwnerAndName.lastIndexOf('/') + 1); String obfDesc = obfOwnerNameAndDesc.substring(obfOwnerNameAndDesc.indexOf('(')); String srgDesc = forwardSRG.mapMethodDescriptor(obfDesc); String srgOwner = srg.classes.get(obfOwner); Set<String> srgMethodOwnersThis = srgMethodOwnersAndDescs.get(srgName); if(srgMethodOwnersThis == null) srgMethodOwnersAndDescs.put(srgName, srgMethodOwnersThis = new HashSet<>()); srgMethodOwnersThis.add(srgOwner+srgDesc); forwardSRG.setMethod(obfOwner, obfName, obfDesc, srgName); reverseSRG.setMethod(srgOwner, srgName, srgDesc, obfName); String[] srgExceptions = excFileData.getExceptionClasses(srgOwner, srgName, srgDesc); if(srgExceptions.length > 0) { List<String> obfExceptions = new ArrayList<>(); for(String s : srgExceptions) obfExceptions.add(reverseSRG.getClass(s)); forwardSRG.setExceptions(obfOwner, obfName, obfDesc, obfExceptions); } } } private void loadCSVMapping(Map<String, String> fieldNames, Map<String, String> methodNames) throws CantLoadMCPMappingException { for(Map.Entry<String, String> entry : fieldNames.entrySet()) { String srgName = entry.getKey(); String mcpName = entry.getValue(); if(srgFieldOwners.get(srgName) == null) System.out.println("Field exists in CSV but not in SRG: "+srgName+" (CSV name: "+mcpName+")"); else { for(String srgOwner : srgFieldOwners.get(srgName)) { String mcpOwner = srgOwner; forwardCSV.setField(srgOwner, srgName, mcpName); reverseCSV.setField(mcpOwner, mcpName, srgName); } } } for(Map.Entry<String, String> entry : methodNames.entrySet()) { String srgName = entry.getKey(); String mcpName = entry.getValue(); if(srgMethodOwnersAndDescs.get(srgName) == null) { System.out.println("Method exists in CSV but not in SRG: "+srgName+" (CSV name: "+mcpName+")"); } else { for(String srgOwnerAndDesc : srgMethodOwnersAndDescs.get(srgName)) { String srgDesc = srgOwnerAndDesc.substring(srgOwnerAndDesc.indexOf('(')); String srgOwner = srgOwnerAndDesc.substring(0, srgOwnerAndDesc.indexOf('(')); String mcpOwner = srgOwner; String mcpDesc = srgDesc; forwardCSV.setMethod(srgOwner, srgName, srgDesc, mcpName); reverseCSV.setMethod(mcpOwner, mcpName, mcpDesc, srgName); } } } } public Mapping getReverseSRG() {return reverseSRG;} public Mapping getReverseCSV() {return reverseCSV;} public Mapping getForwardSRG() {return forwardSRG;} public Mapping getForwardCSV() {return forwardCSV;} public static String getMCVer(File mcpDir) throws IOException { try (Scanner in = new Scanner(new File(mcpDir, "conf/version.cfg"))) { while(in.hasNextLine()) { String line = in.nextLine(); if(line.startsWith("ClientVersion")) return line.split("=")[1].trim(); } return "unknown"; } } }