From df3590d097e28309f7ed2dc3cc3d8413a9f56b49 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 8 Mar 2023 18:33:06 +0100 Subject: [PATCH] feat: mappings can now be taken in as an option, generified SrgMapper --- .../exceptions/MappingNotFoundException.java | 4 +- .../ftbsc/lll/processor/LilleroProcessor.java | 80 ++++--- .../ftbsc/lll/processor/tools/SrgMapper.java | 211 ------------------ .../tools/obfuscation/ObfuscationMapper.java | 167 ++++++++++++++ 4 files changed, 218 insertions(+), 244 deletions(-) delete mode 100644 src/main/java/ftbsc/lll/processor/tools/SrgMapper.java create mode 100644 src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java index 817761b..a1a47d1 100644 --- a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java +++ b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java @@ -1,9 +1,9 @@ package ftbsc.lll.exceptions; -import ftbsc.lll.processor.tools.SrgMapper; +import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; /** - * Thrown upon failure to find the requested mapping within a loaded {@link SrgMapper}. + * Thrown upon failure to find the requested mapping within a loaded {@link ObfuscationMapper}. */ public class MappingNotFoundException extends RuntimeException { diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 7d2645e..c6343a6 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -6,7 +6,7 @@ import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.MappingNotFoundException; import ftbsc.lll.exceptions.TargetNotFoundException; import ftbsc.lll.processor.annotations.*; -import ftbsc.lll.processor.tools.SrgMapper; +import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; import ftbsc.lll.proxies.FieldProxy; import ftbsc.lll.proxies.MethodProxy; @@ -33,6 +33,7 @@ import static ftbsc.lll.processor.tools.ASTUtils.*; */ @SupportedAnnotationTypes("ftbsc.lll.processor.annotations.Patch") @SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedOptions("mappingsFile") public class LilleroProcessor extends AbstractProcessor { /** * A {@link Set} of {@link String}s that will contain the fully qualified names @@ -41,9 +42,35 @@ public class LilleroProcessor extends AbstractProcessor { private final Set generatedInjectors = new HashSet<>(); /** - * A static boolean that should be set to true when ran in a non-obfuscated environment. + * The {@link ObfuscationMapper} used to convert classes and variables + * to their obfuscated equivalent. Will be null when no mapper is in use. */ - public static boolean obfuscatedEnvironment = false; //todo: set this + private ObfuscationMapper mapper; + + /** + * Initializes the processor with the processing environment by + * setting the {@code processingEnv} field to the value of the + * {@code processingEnv} argument. + * @param processingEnv environment to access facilities the tool framework + * provides to the processor + * @throws IllegalStateException if this method is called more than once. + */ + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + String location = processingEnv.getOptions().get("mappingsFile"); + if(location == null) + mapper = null; + else { //TODO: add local file + try { + URL url = new URL(location); + InputStream is = url.openStream(); + mapper = new ObfuscationMapper(new BufferedReader(new InputStreamReader(is, + StandardCharsets.UTF_8)).lines()); + is.close(); + } catch(IOException ignored) {} //TODO: proper handling + } + } /** * Where the actual processing happens. @@ -106,7 +133,7 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the class name and maps it to the correct format. * @param name the fully qualified name of the class to convert - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @implNote De facto, there is never any difference between the SRG and MCP name of a class. * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) * names. However, this method still performs a conversion - just in case there is an @@ -114,15 +141,15 @@ public class LilleroProcessor extends AbstractProcessor { * @return the fully qualified class name * @since 0.3.0 */ - private static String findClassName(String name, SrgMapper mapper) { - return mapper == null ? name : mapper.mapClass(name, obfuscatedEnvironment).replace('/', '.'); + private static String findClassName(String name, ObfuscationMapper mapper) { + return mapper == null ? name : mapper.obfuscateClass(name).replace('/', '.'); } /** * Finds the class name and maps it to the correct format. * @param patchAnn the {@link Patch} annotation containing target class info * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @implNote De facto, there is never any difference between the SRG and MCP name of a class. * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) * names. However, this method still performs a conversion - just in case there is an @@ -130,7 +157,7 @@ public class LilleroProcessor extends AbstractProcessor { * @return the fully qualified class name * @since 0.3.0 */ - private static String findClassName(Patch patchAnn, FindMethod methodAnn, SrgMapper mapper) { + private static String findClassName(Patch patchAnn, FindMethod methodAnn, ObfuscationMapper mapper) { String fullyQualifiedName = methodAnn == null || methodAnn.parent() == Object.class ? getClassFullyQualifiedName(patchAnn.value()) @@ -141,11 +168,11 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the class name and maps it to the correct format. * @param patchAnn the {@link Patch} annotation containing target class info - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @return the internal class name * @since 0.3.0 */ - private static String findClassName(Patch patchAnn, SrgMapper mapper) { + private static String findClassName(Patch patchAnn, ObfuscationMapper mapper) { return findClassName(patchAnn, null, mapper); } @@ -153,12 +180,12 @@ public class LilleroProcessor extends AbstractProcessor { * Finds the member name and maps it to the correct format. * @param parentFQN the already mapped FQN of the parent class * @param memberName the name of the member - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @return the internal class name * @since 0.3.0 */ - private static String findMemberName(String parentFQN, String memberName, SrgMapper mapper) { - return mapper == null ? memberName : mapper.mapMember(parentFQN, memberName, obfuscatedEnvironment); + private static String findMemberName(String parentFQN, String memberName, ObfuscationMapper mapper) { + return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName); } /** @@ -166,11 +193,11 @@ public class LilleroProcessor extends AbstractProcessor { * @param parentFQN the already mapped FQN of the parent class * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @return the internal class name * @since 0.3.0 */ - private static String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, SrgMapper mapper) { + private static String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { String methodName = methodAnn == null ? stub.getSimpleName().toString() : methodAnn.name(); try { methodName = findMemberName(parentFQN, methodName, mapper); @@ -188,11 +215,11 @@ public class LilleroProcessor extends AbstractProcessor { * @param patchAnn the {@link Patch} annotation containing target class info * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link ObfuscationMapper} to use * @return the internal class name * @since 0.3.0 */ - private static String findMethodName(Patch patchAnn, FindMethod methodAnn, ExecutableElement stub, SrgMapper mapper) { + private static String findMethodName(Patch patchAnn, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { return findMethodName(findClassName(patchAnn, methodAnn, mapper), methodAnn, stub, mapper); } @@ -250,13 +277,13 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the real method corresponding to a stub. * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link ObfuscationMapper} to use * @return the desired method, if it exists * @throws AmbiguousDefinitionException if it finds more than one candidate * @throws TargetNotFoundException if it finds no valid candidate * @since 0.3.0 */ - private ExecutableElement findRealMethod(ExecutableElement stub, SrgMapper mapper) { + private ExecutableElement findRealMethod(ExecutableElement stub, ObfuscationMapper mapper) { Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); FindMethod findAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info Target target = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled @@ -272,12 +299,12 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the real field corresponding to a stub. * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link ObfuscationMapper} to use * @return the desired method, if it exists * @throws TargetNotFoundException if it finds no valid candidate * @since 0.3.0 */ - private VariableElement findField(ExecutableElement stub, SrgMapper mapper) { + private VariableElement findField(ExecutableElement stub, ObfuscationMapper mapper) { Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); FindField fieldAnn = stub.getAnnotation(FindField.class); String parentName = findClassName(getClassFullyQualifiedName( @@ -307,15 +334,6 @@ public class LilleroProcessor extends AbstractProcessor { * @param cl the {@link TypeElement} for the given class */ private void generateInjectors(TypeElement cl) { - SrgMapper mapper = null; - try { //TODO: cant we get it from local? - URL url = new URL("https://data.fantabos.co/output.tsrg"); - InputStream is = url.openStream(); - mapper = new SrgMapper(new BufferedReader(new InputStreamReader(is, - StandardCharsets.UTF_8)).lines()); - is.close(); - } catch(IOException ignored) {} //TODO: proper handling - //find class information Patch patchAnn = cl.getAnnotation(Patch.class); String targetClassSrgName = findClassName(patchAnn, mapper); @@ -481,7 +499,7 @@ public class LilleroProcessor extends AbstractProcessor { * @return a {@link List} of method specs * @since 0.2.0 */ - private List generateRequestedProxies(TypeElement cl, SrgMapper mapper) { + private List generateRequestedProxies(TypeElement cl, ObfuscationMapper mapper) { List generated = new ArrayList<>(); findAnnotatedMethods(cl, FindMethod.class) .stream() diff --git a/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java b/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java deleted file mode 100644 index d3c9f79..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java +++ /dev/null @@ -1,211 +0,0 @@ -package ftbsc.lll.processor.tools; - -import ftbsc.lll.exceptions.MappingNotFoundException; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; - -/** - * Parses a .tsrg file into a mapper capable of converting from - * deobfuscated names to SRG names. - * Obviously, it may only be used at runtime if the .tsrg file is - * included in the resources. However, in that case, I'd recommend - * using the built-in Forge one and refrain from including an extra - * resource for no good reason. - * @since 0.2.0 - */ -public class SrgMapper { - - /** - * A Map using the deobfuscated names as keys, - * holding information for that Srg class as value. - */ - private final Map mapper = new HashMap<>(); - - /** - * The public constructor. - * Should be passed a {@link Stream} of Strings, one representing each line. - * Whether they contain line endings or not is irrelevant. - * @param str a {@link Stream} of strings - */ - public SrgMapper(Stream str) { - AtomicReference currentClass = new AtomicReference<>(""); - str.forEach(l -> { - if(l.startsWith("\t")) - mapper.get(currentClass.get()).addMember(l); - else { - ObfuscationData s = new ObfuscationData(l); - currentClass.set(s.mcpName); - mapper.put(s.mcpName, s); - } - }); - } - - /** - * Gets the SRG-obfuscated name of the class. - * @param mcp the MCP (deobfuscated) internal name of the desired class - * @return the SRG name of the class - * @throws MappingNotFoundException if no mapping is found - */ - public String getSrgClass(String mcp) { - ObfuscationData data = mapper.get(mcp); - if(data == null) - throw new MappingNotFoundException(mcp); - else return data.srgName; - } - - /** - * Gets the MCP (deobfuscated) name of the class. - * Due to how it's implemented, it's considerably less efficient than its - * opposite operation. - * @param srg the SRG-obfuscated internal name of the desired class - * @return the MCP name of the class - */ - public String getMcpClass(String srg) { - ObfuscationData data = getObfuscationData(srg); - return data.mcpName; - } - - /** - * Gets one between the SRG and MCP names. - * @param name the internal name of the desired class in either format - * @param obf whether it should return the obfuscated name - * @return a {@link String} containing the internal name of the class - * @throws MappingNotFoundException if no mapping is found - * @since 0.3.0 - */ - public String mapClass(String name, boolean obf) { - String srg; - try { - srg = this.getSrgClass(name); - } catch(MappingNotFoundException e) { - srg = name; - name = this.getMcpClass(srg); - } - if(obf) return srg; - else return name; - } - - /** - * Gets the SRG-obfuscated name of a class member (field or method). - * The method signature must be in this format: "methodName methodDescriptor", - * with a space, because that's how it is in .tsrg files. - * @param mcpClass the MCP (deobfuscated) internal name of the container class - * @param member the field name or method signature - * @return the SRG name of the given member - * @throws MappingNotFoundException if no mapping is found - */ - public String getSrgMember(String mcpClass, String member) { - ObfuscationData data = mapper.get(mcpClass); - if(data == null) - throw new MappingNotFoundException(mcpClass + "::" + member); - return data.members.get(member); - } - - /** - * Gets the MCP (deobfuscated) name of the given member. - * Due to how it's implemented, it's considerably less efficient than its - * opposite operation. - * @param srgClass the SRG-obfuscated internal name of the container class - * @param member the field name or method signature - * @return the MCP name of the given member - */ - public String getMcpMember(String srgClass, String member) { - ObfuscationData data = getObfuscationData(srgClass); - for(String mcp : data.members.keySet()) - if(data.members.get(mcp).equals(member)) - return mcp; - return null; - } - - /** - * Obfuscates or deobfuscates a member, given one of its names and the effective. - * @param className the internal or fully qualified name of the container class - * @param memberName the member of the class - * @param obf whether it should return the obfuscated name - * @return the mapped member name - * @throws MappingNotFoundException if no mapping is found - * @since 0.3.0 - */ - public String mapMember(String className, String memberName, boolean obf) { - className = className.replace('.', '/'); - String effectiveClassName = this.mapClass(className, obf); - String srgMemberName; - try { - srgMemberName = this.getSrgMember(effectiveClassName, memberName); - } catch(MappingNotFoundException e) { - srgMemberName = memberName; - memberName = this.getMcpMember(effectiveClassName, memberName); - } - if(obf) return srgMemberName; - else return memberName; - } - - /** - * Used internally. Gets the obfuscation data corresponding to the given SRG name. - * @return the desired {@link ObfuscationData} object - * @throws MappingNotFoundException if no {@link ObfuscationData} object is found - */ - private ObfuscationData getObfuscationData(String srg) { - for(ObfuscationData s : mapper.values()) - if(s.srgName.equals(srg)) - return s; - throw new MappingNotFoundException(srg); - } - - /** - * Private class used internally for storing information about each - * class. It's private because there is no good reason anyone would - * want to access this outside of this class. - */ - private static class ObfuscationData { - /** - * The MCP internal name (FQN with '/' instad of '.') of the class. - */ - private final String mcpName; - - /** - * The SRG internal name (FQN with '/' instad of '.') of the class. - */ - private final String srgName; - - /** - * A {@link Map} tying each member's deobfuscated name or signature to its - * SRG name. - */ - private final Map members; - - - /** - * The constructor. It takes in the line where the class is declared, - * which looks something like this: - * {@code internal/name/mcp internal/name/srg } - * @param s the String represeting the declaration line - */ - private ObfuscationData(String s) { - String[] split = s.trim().split(" "); - this.mcpName = split[0]; - this.srgName = split[1]; - this.members = new HashMap<>(); - } - - /** - * Adds a member to the target class. It takes in the line where the - * member is declared. - * For fields it looks like this: - * {@code fieldMcpName field_srg_name} - * For methods it looks like this: - * {@code methodName methodDescriptor method_srg_name} - * @param s the String representing the declaration line - */ - public void addMember(String s) { - String[] split = s.trim().split(" "); - if(split.length == 2) //field - members.put(split[0], split[1]); - else if (split.length == 3) //method - members.put(split[0] + " " + split[1], split[2]); - } - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java new file mode 100644 index 0000000..1d19c6b --- /dev/null +++ b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java @@ -0,0 +1,167 @@ +package ftbsc.lll.processor.tools.obfuscation; + +import ftbsc.lll.exceptions.MappingNotFoundException; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +/** + * Parses a .tsrg file into a mapper capable of converting from + * deobfuscated names to obfuscated ones. + * Obviously, it may only be used at runtime if the .tsrg file is + * included in the resources. However, in that case, I'd recommend + * using the built-in Forge one and refrain from including an extra + * resource for no good reason. + * TODO: CSV format + * @since 0.2.0 + */ +public class ObfuscationMapper { + + /** + * A Map using the deobfuscated names as keys, + * holding information for that class as value. + */ + private final Map mapper = new HashMap<>(); + + /** + * The public constructor. + * Should be passed a {@link Stream} of Strings, one representing each line. + * Whether they contain line endings or not is irrelevant. + * @param str a {@link Stream} of strings + */ + public ObfuscationMapper(Stream str) { + AtomicReference currentClass = new AtomicReference<>(""); + str.forEach(l -> { + if(l.startsWith("\t")) + mapper.get(currentClass.get()).addMember(l); + else { + String[] sp = l.split(" "); + ObfuscationData s = new ObfuscationData(sp[0], sp[1]); + currentClass.set(s.unobf); + mapper.put(s.unobf, s); + } + }); + } + + /** + * Gets the obfuscated name of the class. + * @param name the unobfuscated internal name of the desired class + * @return the obfuscated name of the class + * @throws MappingNotFoundException if no mapping is found + */ + public String obfuscateClass(String name) { + ObfuscationData data = mapper.get(name); + if(data == null) + throw new MappingNotFoundException(name); + else return data.obf; + } + + /** + * Gets the unobfuscated name of the class. + * Due to how it's implemented, it's considerably less efficient than its + * opposite operation. + * @param obfName the obfuscated internal name of the desired class + * @return the deobfuscated name of the class + */ + public String deobfuscateClass(String obfName) { + ObfuscationData data = getObfuscationData(obfName); + return data.unobf; + } + + /** + * Gets the obfuscated name of a class member (field or method). + * The method signature must be in this format: "methodName methodDescriptor", + * with a space, because that's how it is in .tsrg files. + * @param parentName the unobfuscated internal name of the parent class + * @param memberName the field name or method signature + * @return the obfuscated name of the given member + * @throws MappingNotFoundException if no mapping is found + */ + public String obfuscateMember(String parentName, String memberName) { + ObfuscationData data = mapper.get(parentName); + if(data == null) + throw new MappingNotFoundException(parentName + "::" + memberName); + return data.members.get(memberName); + } + + /** + * Gets the unobfuscated name of the given member. + * Due to how it's implemented, it's considerably less efficient than its + * opposite operation. + * @param parentObf the obfuscated internal name of the container class + * @param memberObf the field name or method signature + * @return the deobfuscated name of the given member + */ + public String deobfuscateMember(String parentObf, String memberObf) { + ObfuscationData data = getObfuscationData(parentObf); + for(String unobf : data.members.keySet()) + if(data.members.get(unobf).equals(memberObf)) + return unobf; + return null; + } + + /** + * Used internally. Gets the obfuscation data corresponding to the given obfuscated class name. + * @param classObfuscatedName the internal name of the obfuscated class + * @return the desired {@link ObfuscationData} object + * @throws MappingNotFoundException if no {@link ObfuscationData} object is found + */ + private ObfuscationData getObfuscationData(String classObfuscatedName) { + for(ObfuscationData s : mapper.values()) + if(s.obf.equals(classObfuscatedName)) + return s; + throw new MappingNotFoundException(classObfuscatedName); + } + + /** + * Private class used internally for storing information about each + * class. It's private because there is no good reason anyone would + * want to access this outside of this class. + */ + private static class ObfuscationData { + /** + * The unobfuscated name (FQN with '/' instad of '.') of the class. + */ + private final String unobf; + + /** + * The obfuscated internal name (FQN with '/' instad of '.') of the class. + */ + private final String obf; + + /** + * A {@link Map} tying each member's name or signature to its + * obfuscated counterpart. + */ + private final Map members; + + + /** + * The constructor. It takes in the names (obfuscated and non-obfuscated) + * of a class. + * @param unobf the unobfuscated name + * @param obf the obfuscated name + */ + private ObfuscationData(String unobf, String obf) { + this.unobf = unobf; + this.obf = obf; + this.members = new HashMap<>(); + } + + /** + * Adds a member to the target class. + * For fields only the names are required; for methods, + * this takes in the full signature ({@code name + " " + space}). + * @param s the String representing the declaration line + */ + public void addMember(String s) { + String[] split = s.trim().split(" "); + if(split.length == 2) //field + members.put(split[0], split[1]); + else if (split.length == 3) //method + members.put(split[0] + " " + split[1], split[2]); + } + } +}